2012-10-15 02:38:36 +00:00
/*
* uhub - A tiny ADC p2p connection hub
2014-05-14 09:38:08 +00:00
* Copyright ( C ) 2007 - 2014 , Jan Vidar Krey
2012-10-15 02:38:36 +00:00
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*
*/
# include "plugin_api/handle.h"
# include "plugin_api/command_api.h"
# include "util/config_token.h"
# include <sqlite3.h>
# include "util/memory.h"
# include "util/misc.h"
# include "util/list.h"
# include "util/cbuffer.h"
# define MAX_HISTORY_SIZE 16384
struct chat_history_data
{
size_t history_max ; ///<<< "the maximum number of chat messages kept in history."
size_t history_default ; ///<<< "the default number of chat messages returned if no limit was provided"
size_t history_connect ; ///<<< "the number of chat messages provided when users connect to the hub."
sqlite3 * db ; ///<<< "The chat history storage database."
struct plugin_command_handle * command_history_handle ; ///<<< "A handle to the !history command."
struct plugin_command_handle * command_historycleanup_handle ; ///<<< "A handle to the !historycleanup command."
} ;
struct chat_history_line
{
char message [ MAX_HISTORY_SIZE ] ;
char from [ MAX_NICK_LEN ] ;
char time [ 20 ] ;
} ;
static int null_callback ( void * ptr , int argc , char * * argv , char * * colName ) { return 0 ; }
static const char * sql_escape_string ( const char * str )
{
static char out [ 1024 ] ;
size_t i = 0 ;
size_t n = 0 ;
for ( ; n < strlen ( str ) ; n + + )
{
if ( str [ n ] = = ' \' ' )
out [ i + + ] = ' \' ' ;
out [ i + + ] = str [ n ] ;
}
out [ i + + ] = ' \0 ' ;
return out ;
}
static int sql_execute ( struct chat_history_data * sql , int ( * callback ) ( void * ptr , int argc , char * * argv , char * * colName ) , void * ptr , const char * sql_fmt , . . . )
{
va_list args ;
char query [ 1024 ] ;
char * errMsg ;
int rc ;
va_start ( args , sql_fmt ) ;
vsnprintf ( query , sizeof ( query ) , sql_fmt , args ) ;
rc = sqlite3_exec ( sql - > db , query , callback , ptr , & errMsg ) ;
if ( rc ! = SQLITE_OK )
{
sqlite3_free ( errMsg ) ;
return - rc ;
}
rc = sqlite3_changes ( sql - > db ) ;
return rc ;
}
static void create_tables ( struct plugin_handle * plugin )
{
const char * table_create = " CREATE TABLE IF NOT EXISTS chat_history "
" ( "
" from_nick CHAR, "
" message TEXT, "
" time TIMESTAMP DEFAULT (DATETIME('NOW')) "
" ); " ;
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
sql_execute ( data , null_callback , NULL , table_create ) ;
}
/**
* Add a chat message to history .
*/
static void history_add ( struct plugin_handle * plugin , struct plugin_user * from , const char * message , int flags )
{
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
char * history_line = strdup ( sql_escape_string ( message ) ) ;
char * history_nick = strdup ( sql_escape_string ( from - > nick ) ) ;
sql_execute ( data , null_callback , NULL , " INSERT INTO chat_history (from_nick, message) VALUES('%s', '%s');DELETE FROM chat_history WHERE time <= (SELECT time FROM chat_history ORDER BY time DESC LIMIT %d,1); " , history_nick , history_line , data - > history_max ) ;
hub_free ( history_line ) ;
hub_free ( history_nick ) ;
}
/**
* Obtain messages from the chat history as a linked list
*/
static int get_messages_callback ( void * ptr , int argc , char * * argv , char * * colName )
{
struct linked_list * messages = ( struct linked_list * ) ptr ;
struct chat_history_line * line = hub_malloc ( sizeof ( struct chat_history_line ) ) ;
int i = 0 ;
memset ( line , 0 , sizeof ( struct chat_history_line ) ) ;
for ( ; i < argc ; i + + ) {
if ( strcmp ( colName [ i ] , " from_nick " ) = = 0 )
strncpy ( line - > from , argv [ i ] , MAX_NICK_LEN ) ;
else if ( strcmp ( colName [ i ] , " message " ) = = 0 )
strncpy ( line - > message , argv [ i ] , MAX_HISTORY_SIZE ) ;
else if ( strcmp ( colName [ i ] , " time " ) = = 0 )
strncpy ( line - > time , argv [ i ] , 20 ) ;
}
list_append ( messages , line ) ;
return 0 ;
}
void user_login ( struct plugin_handle * plugin , struct plugin_user * user )
{
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
struct cbuffer * buf = NULL ;
struct linked_list * found = ( struct linked_list * ) list_create ( ) ;
sql_execute ( data , get_messages_callback , found , " SELECT * FROM chat_history ORDER BY time DESC LIMIT 0,%d; " , ( int ) data - > history_connect ) ;
if ( data - > history_connect > 0 & & list_size ( found ) > 0 )
{
buf = cbuf_create ( MAX_HISTORY_SIZE ) ;
cbuf_append ( buf , " Chat history: \n \n " ) ;
struct chat_history_line * history_line ;
history_line = ( struct chat_history_line * ) list_get_last ( found ) ;
while ( history_line )
{
cbuf_append_format ( buf , " [%s] <%s> %s \n " , history_line - > time , history_line - > from , history_line - > message ) ;
list_remove ( found , history_line ) ;
hub_free ( history_line ) ;
history_line = ( struct chat_history_line * ) list_get_last ( found ) ;
}
plugin - > hub . send_message ( plugin , user , cbuf_get ( buf ) ) ;
cbuf_destroy ( buf ) ;
}
list_clear ( found , & hub_free ) ;
list_destroy ( found ) ;
}
/**
* The callback function for handling the ! history command .
*/
static int command_history ( struct plugin_handle * plugin , struct plugin_user * user , struct plugin_command * cmd )
{
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
struct cbuffer * buf = cbuf_create ( MAX_HISTORY_SIZE ) ;
struct linked_list * found = ( struct linked_list * ) list_create ( ) ;
struct plugin_command_arg_data * arg = plugin - > hub . command_arg_next ( plugin , cmd , plugin_cmd_arg_type_integer ) ;
int maxlines ;
if ( arg )
maxlines = arg - > data . integer ;
else
maxlines = data - > history_default ;
sql_execute ( data , get_messages_callback , found , " SELECT * FROM chat_history ORDER BY time DESC LIMIT 0,%d; " , maxlines ) ;
size_t linecount = list_size ( found ) ;
if ( linecount > 0 )
{
cbuf_append_format ( buf , " *** %s: Chat History: \n \n " , cmd - > prefix ) ;
struct chat_history_line * history_line ;
history_line = ( struct chat_history_line * ) list_get_last ( found ) ;
while ( history_line )
{
cbuf_append_format ( buf , " [%s] <%s> %s \n " , history_line - > time , history_line - > from , history_line - > message ) ;
list_remove ( found , history_line ) ;
hub_free ( history_line ) ;
history_line = ( struct chat_history_line * ) list_get_last ( found ) ;
}
}
else
{
cbuf_append_format ( buf , " *** %s: No messages found. " , cmd - > prefix ) ;
}
plugin - > hub . send_message ( plugin , user , cbuf_get ( buf ) ) ;
cbuf_destroy ( buf ) ;
list_clear ( found , & hub_free ) ;
list_destroy ( found ) ;
return 0 ;
}
static int command_historycleanup ( struct plugin_handle * plugin , struct plugin_user * user , struct plugin_command * cmd )
{
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
struct cbuffer * buf = cbuf_create ( 128 ) ;
int rc = 0 ;
rc = sql_execute ( data , null_callback , NULL , " DELETE FROM chat_history; " ) ;
if ( ! rc )
cbuf_append_format ( buf , " *** %s: Unable to clean chat history table. " , cmd - > prefix ) ;
else
cbuf_append_format ( buf , " *** %s: Cleaned chat history table. " , cmd - > prefix ) ;
plugin - > hub . send_message ( plugin , user , cbuf_get ( buf ) ) ;
cbuf_destroy ( buf ) ;
return 0 ;
}
static void set_error_message ( struct plugin_handle * plugin , const char * msg )
{
plugin - > error_msg = msg ;
}
static struct chat_history_data * parse_config ( const char * line , struct plugin_handle * plugin )
{
struct chat_history_data * data = ( struct chat_history_data * ) hub_malloc_zero ( sizeof ( struct chat_history_data ) ) ;
struct cfg_tokens * tokens = cfg_tokenize ( line ) ;
char * token = cfg_token_get_first ( tokens ) ;
uhub_assert ( data ! = NULL ) ;
data - > history_max = 200 ;
data - > history_default = 25 ;
data - > history_connect = 5 ;
while ( token )
{
struct cfg_settings * setting = cfg_settings_split ( token ) ;
if ( ! setting )
{
set_error_message ( plugin , " Unable to parse startup parameters " ) ;
cfg_tokens_free ( tokens ) ;
hub_free ( data ) ;
return 0 ;
}
if ( strcmp ( cfg_settings_get_key ( setting ) , " file " ) = = 0 )
{
if ( ! data - > db )
{
if ( sqlite3_open ( cfg_settings_get_value ( setting ) , & data - > db ) )
{
cfg_tokens_free ( tokens ) ;
cfg_settings_free ( setting ) ;
hub_free ( data ) ;
set_error_message ( plugin , " Unable to open database file " ) ;
return 0 ;
}
}
}
else if ( strcmp ( cfg_settings_get_key ( setting ) , " history_max " ) = = 0 )
{
data - > history_max = ( size_t ) uhub_atoi ( cfg_settings_get_value ( setting ) ) ;
}
else if ( strcmp ( cfg_settings_get_key ( setting ) , " history_default " ) = = 0 )
{
data - > history_default = ( size_t ) uhub_atoi ( cfg_settings_get_value ( setting ) ) ;
}
else if ( strcmp ( cfg_settings_get_key ( setting ) , " history_connect " ) = = 0 )
{
data - > history_connect = ( size_t ) uhub_atoi ( cfg_settings_get_value ( setting ) ) ;
}
else
{
set_error_message ( plugin , " Unknown startup parameters given " ) ;
cfg_tokens_free ( tokens ) ;
cfg_settings_free ( setting ) ;
hub_free ( data ) ;
return 0 ;
}
cfg_settings_free ( setting ) ;
token = cfg_token_get_next ( tokens ) ;
}
cfg_tokens_free ( tokens ) ;
return data ;
}
int plugin_register ( struct plugin_handle * plugin , const char * config )
{
struct chat_history_data * data ;
PLUGIN_INITIALIZE ( plugin , " SQLite chat history plugin " , " 1.0 " , " Provide a global chat history log. " ) ;
plugin - > funcs . on_user_chat_message = history_add ;
plugin - > funcs . on_user_login = user_login ;
data = parse_config ( config , plugin ) ;
if ( ! data )
return - 1 ;
plugin - > ptr = data ;
create_tables ( plugin ) ;
data - > command_history_handle = ( struct plugin_command_handle * ) hub_malloc ( sizeof ( struct plugin_command_handle ) ) ;
PLUGIN_COMMAND_INITIALIZE ( data - > command_history_handle , plugin , " history " , " ?N " , auth_cred_guest , & command_history , " Show chat message history. " ) ;
plugin - > hub . command_add ( plugin , data - > command_history_handle ) ;
data - > command_historycleanup_handle = ( struct plugin_command_handle * ) hub_malloc ( sizeof ( struct plugin_command_handle ) ) ;
PLUGIN_COMMAND_INITIALIZE ( data - > command_historycleanup_handle , plugin , " historycleanup " , " " , auth_cred_admin , & command_historycleanup , " Clean chat message history. " ) ;
plugin - > hub . command_add ( plugin , data - > command_historycleanup_handle ) ;
return 0 ;
}
int plugin_unregister ( struct plugin_handle * plugin )
{
struct chat_history_data * data = ( struct chat_history_data * ) plugin - > ptr ;
if ( data )
{
sqlite3_close ( data - > db ) ;
plugin - > hub . command_del ( plugin , data - > command_history_handle ) ;
plugin - > hub . command_del ( plugin , data - > command_historycleanup_handle ) ;
hub_free ( data - > command_history_handle ) ;
hub_free ( data - > command_historycleanup_handle ) ;
hub_free ( data ) ;
}
return 0 ;
}