From 406c308308e4d131475a692cd425cb156e0776f1 Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 17 Feb 2012 00:40:58 +0100 Subject: [PATCH] added stats module for neonserv.krypton-bouncer.de stats --- Makefile.am | 6 +- database.sql | 11 +++ database.upgrade.sql | 7 ++ neonserv.example.conf | 8 ++ src/ClientSocket.c | 5 + src/ClientSocket.h | 1 + src/IRCParser.c | 40 ++++++++ src/modules.c | 14 ++- src/modules.h | 2 + src/modules/module.h | 1 + src/modules/stats.mod/module.c | 167 +++++++++++++++++++++++++++++++++ src/mysqlConn.c | 2 +- src/version.h | 2 +- 13 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 src/modules/stats.mod/module.c diff --git a/Makefile.am b/Makefile.am index 03edbe3..c312545 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ checkversion: cd src && chmod +x version.sh && ./version.sh && cd .. noinst_PROGRAMS = neonserv -noinst_LTLIBRARIES = libDummyServ.la libfuncmds.la libglobalcmd.la libNeonHelp.la libNeonServ.la libNeonSpam.la +noinst_LTLIBRARIES = libDummyServ.la libfuncmds.la libglobalcmd.la libNeonHelp.la libNeonServ.la libNeonSpam.la libstats.la libDummyServ_la_SOURCES = src/modules/DummyServ.mod/bot_DummyServ.c \ src/modules/DummyServ.mod/module.c @@ -142,6 +142,10 @@ libNeonSpam_la_SOURCES = src/modules/NeonSpam.mod/bot_NeonSpam.c \ libNeonSpam_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined libNeonSpam_la_LIBADD = $(MYSQL_LIBS) +libstats_la_SOURCES = src/modules/stats.mod/module.c +libstats_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libstats_la_LIBADD = $(MYSQL_LIBS) + neonserv_SOURCES = src/version.c \ src/EventLogger.c \ src/IRCEvents.c \ diff --git a/database.sql b/database.sql index fbc3b40..d3d9156 100644 --- a/database.sql +++ b/database.sql @@ -401,3 +401,14 @@ CREATE TABLE IF NOT EXISTS `helpserv_settings` ( PRIMARY KEY (`helpserv_botid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `settings` +-- + +CREATE TABLE IF NOT EXISTS `settings` ( + `name` varchar(100) NOT NULL, + `value` text NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/database.upgrade.sql b/database.upgrade.sql index cad2b73..7d08cd8 100644 --- a/database.upgrade.sql +++ b/database.upgrade.sql @@ -140,3 +140,10 @@ ADD `user_lastcheck` INT( 20 ) NOT NULL; -- version: 15 +CREATE TABLE IF NOT EXISTS `settings` ( + `name` varchar(100) NOT NULL, + `value` text NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- version: 16 diff --git a/neonserv.example.conf b/neonserv.example.conf index 5be3085..ba99ed7 100644 --- a/neonserv.example.conf +++ b/neonserv.example.conf @@ -77,5 +77,13 @@ "enabled" = 1; "protected" = 0; }; + "libstats" { + "enabled" = 1; + "protected" = 0; + "hide_networkname" = 0; + "hide_botnick" = 0; + "hide_chancount" = 0; + "hide_usercount" = 0; + }; }; diff --git a/src/ClientSocket.c b/src/ClientSocket.c index 21a191e..d1f80ff 100644 --- a/src/ClientSocket.c +++ b/src/ClientSocket.c @@ -74,6 +74,7 @@ struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pas client->ident = strdup(ident); client->realname = strdup(realname); client->user = NULL; + client->network_name = NULL; client->flags = 0; client->bufferpos = 0; client->traffic_in = 0; @@ -344,6 +345,8 @@ static void destroy_socket(struct ClientSocket *client, int free_socket) { free(client->bind); if(client->pass) free(client->pass); + if(client->network_name) + free(client->network_name); free(client); } else if(client->flags & SOCKET_FLAG_FAST_JUMP) { client->flags &= ~SOCKET_FLAG_FAST_JUMP; @@ -580,6 +583,8 @@ void free_sockets() { free(client->bind); if(client->pass) free(client->pass); + if(client->network_name) + free(client->network_name); free(client); } free(sockets); diff --git a/src/ClientSocket.h b/src/ClientSocket.h index 40b65ba..a65dfdf 100644 --- a/src/ClientSocket.h +++ b/src/ClientSocket.h @@ -55,6 +55,7 @@ struct ClientSocket { char *ident; char *realname; struct UserNode *user; + char *network_name; unsigned long traffic_in; unsigned long traffic_out; time_t connection_time; diff --git a/src/IRCParser.c b/src/IRCParser.c index da4f434..220283e 100644 --- a/src/IRCParser.c +++ b/src/IRCParser.c @@ -721,10 +721,50 @@ static void client_renamed(struct ClientSocket *client) { } } +static void raw_005_network(struct ClientSocket *client, char *value) { + if(!value) return; + //check all other networknames + //if they are NOT simular to value throw a warning + SYNCHRONIZE(cache_sync); //all bots connect to the same time so there is a higher chance that this code is running on multiple threads at the same time + if(client->network_name) + free(client->network_name); + client->network_name = strdup(value); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot == client) continue; + if(!bot->network_name) continue; + if(stricmp(bot->network_name, value)) { + putlog(LOGLEVEL_ERROR, "WARNING: Network name '%s' (%s) differs from '%s' (%s)! Connecting to multiple IRC-Networks with one instance is NOT supported!\n", client->network_name, client->nick, bot->network_name, bot->nick); + break; + } + } + DESYNCHRONIZE(cache_sync); +} + +static IRC_CMD(raw_005) { + char *ptr1 = merge_argv(argv, 1, argc); + char *ptr2, *name, *value; + do { + ptr2 = strchr(ptr1, ' '); + if(ptr2) + *ptr2 = '\0'; + name = ptr1; + if((value = strchr(ptr1, '='))) { + *value = '\0'; + value++; + } + if(!stricmp(name, "NETWORK")) raw_005_network(client, value); + if(ptr2) + ptr1 = ptr2 + 1; + } while(ptr2); + return 1; +} + void init_parser() { //all the raws we receive... register_irc_function("437", raw_437); register_irc_function("002", raw_002); + register_irc_function("005", raw_005); register_irc_function("251", raw_251); register_irc_function("254", raw_254); register_irc_function("324", raw_324); diff --git a/src/modules.c b/src/modules.c index adbe37d..d024c3e 100644 --- a/src/modules.c +++ b/src/modules.c @@ -44,6 +44,7 @@ /* 170-180 */ #include "UserNode.h" /* 181-183 */ #include "WHOHandler.h" /* 184-188 */ #include "version.h" +/* 189 */ /* modules.h */ #define Function void * @@ -247,7 +248,8 @@ void *global_functions[] = { /* 185 */ (Function) get_creation, /* 186 */ (Function) get_revision, /* 187 */ (Function) get_codelines, -/* 188 */ (Function) get_patchlevel +/* 188 */ (Function) get_patchlevel, +/* 189 */ (Function) get_module_name }; static int module_id_counter = 1; @@ -471,3 +473,13 @@ int module_loaded(int module_id) { } return 0; } + +char *get_module_name(int module_id) { + if(!module_id) return NULL; + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(modinfo->module_id == module_id) + return modinfo->name; + } + return NULL; +} diff --git a/src/modules.h b/src/modules.h index 5f6ebb6..cbb496a 100644 --- a/src/modules.h +++ b/src/modules.h @@ -44,6 +44,8 @@ void loop_modules(); void stop_modules(); int module_loaded(int module_id); +/* MODULAR ACCESSIBLE */ char *get_module_name(int module_id); + int ext_load_module(char *name); int ext_unload_module(char *name); int ext_reload_module(char *name); diff --git a/src/modules/module.h b/src/modules/module.h index 73e141d..89bf781 100644 --- a/src/modules/module.h +++ b/src/modules/module.h @@ -218,6 +218,7 @@ extern int module_id; /* 186 */ #define get_revision ((const char * (*)(void))global[186]) /* 187 */ #define get_codelines ((const char * (*)(void))global[187]) /* 188 */ #define get_patchlevel ((const int (*)(void))global[188]) +/* 189 */ #define get_module_name ((char * (*)(int))global[189]) #define MODULE_HEADER(initfunc,startfunc,loopfunc,stopfunc) \ void **global = NULL; \ diff --git a/src/modules/stats.mod/module.c b/src/modules/stats.mod/module.c new file mode 100644 index 0000000..ee8a20d --- /dev/null +++ b/src/modules/stats.mod/module.c @@ -0,0 +1,167 @@ +/* module.c - NeonServ v5.4 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 . + */ +#include "../module.h" +#include "../../timeq.h" +#include "../../ConfigParser.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" + +#define STATS_UPDATE_SECONDS 1800 +#define STATS_UPDATE_HOST "neonserv.krypton-bouncer.de" +#define STATS_UPDATE_PORT 1675 +#define STATS_IDENTIFIER_LEN 10 +#define STATS_IDENTIFIER_POOL "abcdefghijklmnopqrstuvwxyz0123456789#*+-_.:;,!§$%&/()=?[]{}<>|@" +#define STATS_IDENTIFIER_POOL_SIZE 63 + +static TIMEQ_CALLBACK(stats_timer_callback); + +static int module_initialize() { + return 0; +} + +static void module_start(int type) { + if(!timeq_name_exists("stats")) + timeq_add_name("stats", 60, module_id, stats_timer_callback, NULL); +} + +static void module_loop() { + +} + +static void module_stop(int type) { + timeq_del_name("stats"); +} + +static char *get_identifier() { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `value` FROM `settings` WHERE `name` = 'identifier'"); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + srand(time(NULL)); + char identifier[STATS_IDENTIFIER_LEN+1]; + int i; + char *pool = STATS_IDENTIFIER_POOL; + for(i = 0; i < STATS_IDENTIFIER_LEN; i++) { + identifier[i] = pool[(rand() % STATS_IDENTIFIER_POOL_SIZE)]; + } + identifier[i] = 0; + printf_mysql_query("INSERT INTO `settings` (`name`, `value`) VALUES ('identifier', '%s')", escape_string(identifier)); + printf_mysql_query("SELECT `value` FROM `settings` WHERE `name` = 'identifier'"); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row) return NULL; + } + if(strlen(row[0]) < 10) return NULL; + return row[0]; +} + +static TIMEQ_CALLBACK(stats_timer_callback) { + timeq_add_name("stats", STATS_UPDATE_SECONDS, module_id, stats_timer_callback, NULL); + char tmp[200]; + char pkgbuf[1024]; + int pkgpos = 0; + char *modname = get_module_name(module_id); + char *bot_identifier = get_identifier(); + if(!bot_identifier) return; + //build update package + pkgpos += sprintf(pkgbuf + pkgpos, "nsupdate\n%s\n%s.%d %s\n", bot_identifier, NEONSERV_VERSION, get_patchlevel(), (strcmp(get_revision(), "") ? get_revision() : "-")); + sprintf(tmp, "modules/%s/hide_networkname", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->network_name) break; + } + if(bot) + pkgpos += sprintf(pkgbuf + pkgpos, "%s\n", bot->network_name); + else + pkgpos += sprintf(pkgbuf + pkgpos, "?\n"); + } + sprintf(tmp, "modules/%s/hide_botnick", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + struct ClientSocket *bot, *bot1, *bot2, *bot3; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->botid == 1 && (bot->flags & SOCKET_FLAG_PREFERRED)) + bot1 = bot; + else if((bot->flags & SOCKET_FLAG_PREFERRED)) + bot2 = bot; + else + bot3 = bot; + } + if(bot1) + bot = bot1; + else if(bot2) + bot = bot2; + else + bot = bot3; + if(bot) { + pkgpos += sprintf(pkgbuf + pkgpos, "%s!%s@%s %d\n", bot->user->nick, bot->user->ident, bot->host, bot->port); + } else + pkgpos += sprintf(pkgbuf + pkgpos, "?\n"); + } + sprintf(tmp, "modules/%s/hide_chancount", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + int channel_count = getChannelCount(); + pkgpos += sprintf(pkgbuf + pkgpos, "%d\n", channel_count); + } + sprintf(tmp, "modules/%s/hide_usercount", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + int user_count = getUserCount(); + int chanuser_count = getChanUserCount(); + pkgpos += sprintf(pkgbuf + pkgpos, "%d %d\n", user_count, chanuser_count); + } + pkgbuf[pkgpos] = 0; + //send package + #ifndef WIN32 + struct sockaddr_in si_other; + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) return; + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(STATS_UPDATE_PORT); + if (!inet_aton(STATS_UPDATE_HOST, &si_other.sin_addr)) { + struct hostent *host = gethostbyname(STATS_UPDATE_HOST); + if (!host) return; + si_other.sin_addr = *(struct in_addr*)host->h_addr; + } + #else + struct sockaddr_in si_other; + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) return; + si_other.sin_family = AF_INET; + si_other.sin_port = htons(STATS_UPDATE_PORT); + si_other.sin_addr.s_addr = inet_addr(client->host); + if (si_other.sin_addr.s_addr == INADDR_NONE) { + struct hostent *host = gethostbyname(client->host); + if(!host) return; + memcpy(&(si_other.sin_addr), host->h_addr_list[0], 4); + } + #endif + sendto(sock, pkgbuf, pkgpos, 0, &si_other, sizeof(si_other)); + close(sock); +} + +MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); diff --git a/src/mysqlConn.c b/src/mysqlConn.c index a1fb75b..163ae12 100644 --- a/src/mysqlConn.c +++ b/src/mysqlConn.c @@ -16,7 +16,7 @@ */ #include "mysqlConn.h" -#define DATABASE_VERSION "15" +#define DATABASE_VERSION "16" static void show_mysql_error(); diff --git a/src/version.h b/src/version.h index cb1b6fb..85900e2 100644 --- a/src/version.h +++ b/src/version.h @@ -19,7 +19,7 @@ #include "main.h" -#define MODULE_VERSION 1 +#define MODULE_VERSION 2 #ifndef DND_FUNCTIONS extern const char *compilation; -- 2.20.1