From c32e8254ec4ed0d77757e32f8aa5aabcdb494057 Mon Sep 17 00:00:00 2001 From: pk910 Date: Tue, 17 Jan 2012 17:19:20 +0100 Subject: [PATCH] added new auth-check security feature --- database.sql | 2 + database.upgrade.sql | 6 +++ neonserv.example.conf | 11 ++++- src/ClientSocket.c | 53 +++++++++++++++++++++- src/ClientSocket.h | 3 ++ src/DBHelper.c | 40 +++++++++++++++++ src/DBHelper.h | 2 + src/HandleInfoHandler.c | 87 ++++++++++++++++++++++++++++++++++--- src/HandleInfoHandler.h | 2 +- src/WHOHandler.c | 52 ++-------------------- src/WHOHandler.h | 4 -- src/cmd_global_unregister.c | 7 ++- src/main.c | 79 +++++++++++++++++++++++++++++++++ src/mysqlConn.c | 2 +- 14 files changed, 284 insertions(+), 66 deletions(-) diff --git a/database.sql b/database.sql index 2047290..fbc3b40 100644 --- a/database.sql +++ b/database.sql @@ -317,6 +317,8 @@ CREATE TABLE IF NOT EXISTS `users` ( `user_god` tinyint(1) NOT NULL, `user_lang` varchar(6) NOT NULL, `user_reply_privmsg` tinyint(1) NOT NULL, + `user_registered` INT(20) NOT NULL, + `user_lastcheck` INT(20) NOT NULL, PRIMARY KEY (`user_id`), UNIQUE KEY `user_user` (`user_user`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/database.upgrade.sql b/database.upgrade.sql index 4f4697d..cad2b73 100644 --- a/database.upgrade.sql +++ b/database.upgrade.sql @@ -134,3 +134,9 @@ UPDATE `channels` SET `channel_canhalfop` = '150', `channel_gethalfop` = '150' WHERE `channel_name` = 'defaults'; -- version: 14 + +ALTER TABLE `users` ADD `user_registered` INT( 20 ) NOT NULL , +ADD `user_lastcheck` INT( 20 ) NOT NULL; + +-- version: 15 + diff --git a/neonserv.example.conf b/neonserv.example.conf index b032164..a6b3a61 100644 --- a/neonserv.example.conf +++ b/neonserv.example.conf @@ -3,7 +3,7 @@ */ "MySQL" { - "host" = "127.0.0.1 "; + "host" = "127.0.0.1"; "port" = 3306; "user" = "neonserv"; "pass" = "password"; @@ -12,6 +12,15 @@ "General" { "alertchan" = ""; "have_halfop" = 0; + "CheckAuths" { + "enabled" = 1; + "start_time" = 3; //24h format + "duration" = 180; //minutes + "interval" = 2; //seconds + "min_unckecked" = 172800; //check auth only if unchecked for x seconds + //180 miutes, every 2 seconds: 5400 auth checks + "alertchan" = "#krypton.intern"; + }; }; "QServer" { "enabled" = 1; diff --git a/src/ClientSocket.c b/src/ClientSocket.c index 80f4c37..c495777 100644 --- a/src/ClientSocket.c +++ b/src/ClientSocket.c @@ -33,6 +33,12 @@ struct socket_list { #ifdef HAVE_THREADS static pthread_mutex_t synchronized; static pthread_mutex_t synchronized_recv; + +struct ParseOrder { + unsigned long tid; + struct ParseOrder *next; +}; +struct ParseOrder *parse_order = NULL; #endif //the magic list :P @@ -369,6 +375,49 @@ int write_socket(struct ClientSocket *client, char* msg, int len) { return write_socket_force(client, msg, len); } +#if HAVE_THREADS +static void clientsocket_start_of_recv(unsigned long tid) { + SYNCHRONIZE(whohandler_sync); + struct ParseOrder *entry, *last; + for(last = parse_order; last; last = last->next) { + if(last->next == NULL) + break; + } + entry = malloc(sizeof(*entry)); + entry->tid = tid; + entry->next = NULL; + if(last) + last->next = entry; + else + parse_order = entry; + DESYNCHRONIZE(whohandler_sync); +} + +static void clientsocket_end_of_recv(unsigned long tid) { + SYNCHRONIZE(whohandler_sync); + struct ParseOrder *entry, *last = NULL; + for(entry = parse_order; entry; entry = entry->next) { + if(entry->tid == tid) { + if(last) + last->next = entry->next; + else + parse_order = entry->next; + free(entry); + break; + } else + last = entry; + } + DESYNCHRONIZE(whohandler_sync); +} + +int clientsocket_parseorder_top(unsigned long tid) { + if(parse_order && parse_order->tid == tid) + return 1; + else + return 0; +} +#endif + void socket_loop(int timeout_seconds) { if(sockets == NULL) return; int is_synchronized = 1; @@ -446,10 +495,10 @@ void socket_loop(int timeout_seconds) { } is_synchronized = 0; unsigned long tid = syscall(SYS_gettid); - whohandler_start_of_recv(sock, tid); + clientsocket_start_of_recv(tid); DESYNCHRONIZE(synchronized_recv); parse_lines(sock, linesbuf, used); - whohandler_end_of_recv(sock, tid); //WHOHandler hack (unlock WHOQueue mutexes) + clientsocket_end_of_recv(tid); #else int used = parse_lines(sock, sock->buffer, sock->bufferpos); if(used == sock->bufferpos + 1) { diff --git a/src/ClientSocket.h b/src/ClientSocket.h index 4073a23..1ab7139 100644 --- a/src/ClientSocket.h +++ b/src/ClientSocket.h @@ -83,6 +83,9 @@ int close_socket(struct ClientSocket *client); int disconnect_socket(struct ClientSocket *client); int write_socket_force(struct ClientSocket *client, char* msg, int len); int write_socket(struct ClientSocket *client, char* msg, int len); +#ifdef HAVE_THREADS +int clientsocket_parseorder_top(unsigned long tid); +#endif void socket_loop(int timeout_seconds); void putsock(struct ClientSocket *client, const char *text, ...) PRINTF_LIKE(2, 3); struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot); diff --git a/src/DBHelper.c b/src/DBHelper.c index 4f3dd75..980ee7a 100644 --- a/src/DBHelper.c +++ b/src/DBHelper.c @@ -24,6 +24,7 @@ #include "tools.h" #include "IRCEvents.h" #include "HandleInfoHandler.h" +#include "ClientSocket.h" void _loadUserSettings(struct UserNode *user) { SYNCHRONIZE(cache_sync); @@ -301,6 +302,45 @@ static AUTHLOOKUP_CALLBACK(event_user_registered_auth_lookup) { free(cache->oldauth); } +void deleteUser(int userid) { + //simply delete the user + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT a.`chanuser_access`, a.`chanuser_cid`, (SELECT COUNT(*) FROM `chanusers` AS b WHERE b.`chanuser_cid` = a.`chanuser_cid` AND b.`chanuser_access` = 500) FROM `chanusers` AS a WHERE a.`chanuser_uid` = '%d'", userid); + res = mysql_use(); + while((row = mysql_fetch_row(res))) { + if(!strcmp(row[0], "500") && !strcmp(row[2], "1")) { + //unregister channel + printf_mysql_query("SELECT `botid`, `channel_name` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `chanid` = '%s' AND `suspended` = '0'", row[1]); + res2 = mysql_use(); + while((row2 = mysql_fetch_row(res))) { + struct ClientSocket *bot; + int clientid = atoi(row2[0]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == clientid) + putsock(bot, "PART %s :Channel unregistered.", row2[1]); + } + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `chanid` = '%s'", row[1]); + } + } + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_uid` = '%d'", userid); + printf_mysql_query("UPDATE `bans` SET `ban_owner` = 0 WHERE `ban_owner` = '%d'", userid); + printf_mysql_query("UPDATE `donotregister` SET `dnr_user` = 0 WHERE `dnr_user` = '%d'", userid); + printf_mysql_query("UPDATE `bans` SET `ban_owner` = 0 WHERE `ban_owner` = '%d'", userid); + printf_mysql_query("UPDATE `godlog` SET `godlog_uid` = 0 WHERE `godlog_uid` = '%d'", userid); + printf_mysql_query("DELETE FROM `noinvite` WHERE `uid` = '%d'", userid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_to_uid` = 0 WHERE `owner_history_to_uid` = '%d'", userid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = 0 WHERE `owner_history_from_uid` = '%d'", userid); + printf_mysql_query("UPDATE `channels` SET `channel_registrator` = 0 WHERE `channel_registrator` = '%d'", userid); + printf_mysql_query("DELETE FROM `users` WHERE `user_id` = '%d'", userid); + struct UserNode *user; + for(user = getAllUsers(NULL); user; user = getAllUsers(user)) { + if(user->flags & USERFLAG_HAS_USERID) + user->flags &= ~USERFLAG_HAS_USERID; + } +} + void init_DBHelper() { bind_registered(event_user_registered); } diff --git a/src/DBHelper.h b/src/DBHelper.h index caa45bb..01658f4 100644 --- a/src/DBHelper.h +++ b/src/DBHelper.h @@ -39,5 +39,7 @@ char *getBanAffectingMask(struct ChanNode *chan, char *mask); //returns bans tha int renameAccount(char *oldauth, char *newauth); +void deleteUser(int userid); + void init_DBHelper(); #endif \ No newline at end of file diff --git a/src/HandleInfoHandler.c b/src/HandleInfoHandler.c index 99d1965..0508e07 100644 --- a/src/HandleInfoHandler.c +++ b/src/HandleInfoHandler.c @@ -121,6 +121,8 @@ static void recv_notice(struct UserNode *user, struct UserNode *target, char *me char *auth = NULL; int do_match = 0, exists = 0; char *tmp; + time_t registered = time(0); + struct tm *timeinfo; //messages to parse: // Account * has not been registered. // Account information for Skynet: @@ -130,24 +132,99 @@ static void recv_notice(struct UserNode *user, struct UserNode *target, char *me auth = tmp+1; tmp = strstr(auth, "\002"); *tmp = '\0'; - } - if(!match("Account information for *", message)) { - do_match = 1; + } else if(!match("Account information for *", message)) { + do_match = 2; exists = 1; tmp = strstr(message, "\002"); auth = tmp+1; tmp = strstr(auth, "\002"); *tmp = '\0'; + } else if(!match(" Registered on: *", message)) { + do_match = 1; + exists = 1; + tmp = strstr(message, ": "); + tmp += 2; + timeinfo = localtime(®istered); + timeinfo->tm_year = 0; + //parse time + //Sat Nov 19 14:52:57 2011 + tmp = strchr(tmp, ' '); + if(!tmp) goto errparse; + tmp++; + char *tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + if(!stricmp(tmp, "Jan")) + timeinfo->tm_mon = 0; + else if(!stricmp(tmp, "Feb")) + timeinfo->tm_mon = 1; + else if(!stricmp(tmp, "Mar")) + timeinfo->tm_mon = 2; + else if(!stricmp(tmp, "Apr")) + timeinfo->tm_mon = 3; + else if(!stricmp(tmp, "May")) + timeinfo->tm_mon = 4; + else if(!stricmp(tmp, "Jun")) + timeinfo->tm_mon = 5; + else if(!stricmp(tmp, "Jul")) + timeinfo->tm_mon = 6; + else if(!stricmp(tmp, "Aug")) + timeinfo->tm_mon = 7; + else if(!stricmp(tmp, "Sep")) + timeinfo->tm_mon = 8; + else if(!stricmp(tmp, "Oct")) + timeinfo->tm_mon = 9; + else if(!stricmp(tmp, "Nov")) + timeinfo->tm_mon = 10; + else if(!stricmp(tmp, "Dec")) + timeinfo->tm_mon = 11; + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_mday = atoi(tmp); + tmp = tmp2 + 1; + if(*tmp == ' ') tmp++; + tmp2 = strchr(tmp, ':'); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_hour = atoi(tmp); + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ':'); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_min = atoi(tmp); + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_sec = atoi(tmp); + tmp = tmp2 + 1; + timeinfo->tm_year = atoi(tmp) - 1900; + registered = mktime(timeinfo); } + errparse: + if(do_match) { - struct HandleInfoQueueEntry* entry = getNextHandleInfoQueueEntry(bot, 1); + #ifdef HAVE_THREADS + unsigned long tid = syscall(SYS_gettid); + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms + } + #endif + struct HandleInfoQueueEntry* entry = getNextHandleInfoQueueEntry(bot, ((do_match != 2) ? 1 : 0)); if(entry) { + if(do_match == 2) { + free(entry->auth); + entry->auth = strdup(auth); + return; + } authlookup_callback_t *callback; int i; for(i = 0; i < MAXCALLBACKS; i++) { callback = entry->callback[i]; if(!callback) break; - callback(auth, exists, entry->data[i]); + callback(entry->auth, exists, registered, entry->data[i]); } free(entry->auth); free(entry); diff --git a/src/HandleInfoHandler.h b/src/HandleInfoHandler.h index c49cfac..d5bb9bd 100644 --- a/src/HandleInfoHandler.h +++ b/src/HandleInfoHandler.h @@ -22,7 +22,7 @@ struct ClientSocket; struct UserNode; -#define AUTHLOOKUP_CALLBACK(NAME) void NAME(UNUSED_ARG(char *auth), UNUSED_ARG(int exists), UNUSED_ARG(void *data)) +#define AUTHLOOKUP_CALLBACK(NAME) void NAME(UNUSED_ARG(char *auth), UNUSED_ARG(int exists), UNUSED_ARG(time_t registered), UNUSED_ARG(void *data)) typedef AUTHLOOKUP_CALLBACK(authlookup_callback_t); void clear_handleinfoqueue(struct ClientSocket *client); diff --git a/src/WHOHandler.c b/src/WHOHandler.c index 076afd3..f8769de 100644 --- a/src/WHOHandler.c +++ b/src/WHOHandler.c @@ -41,14 +41,6 @@ struct WHOQueueEntry { void *data[MAXCALLBACKS]; }; -#ifdef HAVE_THREADS -struct ParseOrder { - unsigned long tid; - struct ParseOrder *next; -}; -struct ParseOrder *parse_order = NULL; -#endif - static int checkWHOID(struct ClientSocket *client, int whoid) { struct WHOQueueEntry *entry; for(entry = client->whoqueue_first; entry; entry = entry->next) { @@ -122,42 +114,6 @@ void clear_whoqueue(struct ClientSocket *client) { DESYNCHRONIZE(whohandler_sync); } -#if HAVE_THREADS -void whohandler_start_of_recv(struct ClientSocket *client, unsigned long tid) { - SYNCHRONIZE(whohandler_sync); - struct ParseOrder *entry, *last; - for(last = parse_order; last; last = last->next) { - if(last->next == NULL) - break; - } - entry = malloc(sizeof(*entry)); - entry->tid = tid; - entry->next = NULL; - if(last) - last->next = entry; - else - parse_order = entry; - DESYNCHRONIZE(whohandler_sync); -} - -void whohandler_end_of_recv(struct ClientSocket *client, unsigned long tid) { - SYNCHRONIZE(whohandler_sync); - struct ParseOrder *entry, *last = NULL; - for(entry = parse_order; entry; entry = entry->next) { - if(entry->tid == tid) { - if(last) - last->next = entry->next; - else - parse_order = entry->next; - free(entry); - break; - } else - last = entry; - } - DESYNCHRONIZE(whohandler_sync); -} -#endif - void get_userlist(struct ChanNode *chan, userlist_callback_t callback, void *data) { struct ClientSocket *bot; for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { @@ -287,8 +243,8 @@ static void _recv_whohandler_354(struct ClientSocket *client, char **argv, unsig if(entry == NULL) return; #ifdef HAVE_THREADS unsigned long tid = syscall(SYS_gettid); - while(parse_order->tid != tid) { - usleep(5000); //5ms + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms } #endif if(entry->type & WHOQUEUETYPE_USERLIST) { @@ -383,8 +339,8 @@ static void _recv_whohandler_315(struct ClientSocket *client, char **argv, unsig if(entry == NULL) return; #ifdef HAVE_THREADS unsigned long tid = syscall(SYS_gettid); - while(parse_order->tid != tid) { - usleep(5000); //5ms + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms } #endif getNextWHOQueueEntry(client, type, 1); diff --git a/src/WHOHandler.h b/src/WHOHandler.h index d402878..68ad205 100644 --- a/src/WHOHandler.h +++ b/src/WHOHandler.h @@ -30,10 +30,6 @@ typedef USERLIST_CALLBACK(userlist_callback_t); typedef USERAUTH_CALLBACK(userauth_callback_t); void clear_whoqueue(struct ClientSocket *client); -#if HAVE_THREADS -void whohandler_start_of_recv(struct ClientSocket *client, unsigned long tid); -void whohandler_end_of_recv(struct ClientSocket *client, unsigned long tid); -#endif void recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc); void recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc); void get_userlist(struct ChanNode *chan, userlist_callback_t callback, void *data); diff --git a/src/cmd_global_unregister.c b/src/cmd_global_unregister.c index 058f38b..43dab20 100644 --- a/src/cmd_global_unregister.c +++ b/src/cmd_global_unregister.c @@ -42,19 +42,18 @@ CMD_BIND(global_cmd_unregister) { return; } if(client->botid == 0) - printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended`, `bots`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '0' AND `botid` = '%d'", chanid, client->clientid); + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '0' AND `botid` = '%d'", chanid, client->clientid); else - printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended`, `bots`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); res = mysql_use(); if ((row = mysql_fetch_row(res)) == NULL) { reply(getTextBot(), user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); return; } int botid = atoi(row[0]); - int clientid = atoi(row[3]); struct ClientSocket *bot; for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { - if(bot->clientid == botid && (!botid || bot->clientid == clientid)) + if(bot->clientid == botid) break; } if(bot && strcmp(row[2], "1")) { diff --git a/src/main.c b/src/main.c index e8d366a..17980b4 100644 --- a/src/main.c +++ b/src/main.c @@ -43,6 +43,7 @@ static int running, hard_restart; static int statistics_requested_lusers = 0; int statistics_enabled; TIMEQ_CALLBACK(main_statistics); +TIMEQ_CALLBACK(main_checkauths); #ifdef HAVE_THREADS int running_threads; pthread_mutex_t cache_sync; @@ -165,6 +166,8 @@ main: if(!update_minutes) update_minutes = 2; timeq_add(update_minutes * 60 + 10, main_statistics, NULL); + timeq_add(90, main_checkauths, NULL); + int worker_threads = get_int_field("General.worker_threads"); if(!worker_threads) worker_threads = 1; @@ -262,6 +265,82 @@ void reload_config() { loadConfig("neonserv.conf"); } +static int getCurrentSecondsOfDay() { + time_t now = time(0); + struct tm *timeofday = localtime(&now); + int seconds = 0; + seconds += timeofday->tm_hour * 3600; + seconds += timeofday->tm_min * 60; + seconds += timeofday->tm_sec; + return seconds; +} + +static AUTHLOOKUP_CALLBACK(main_checkauths_callback) { + //check if registered is still valid + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id`, `user_registered` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(!exists || (strcmp(row[1], "0") && registered != atoi(row[1]))) { + //User is no longer valid! Delete it... + deleteUser(atoi(row[0])); + char *alertchan = get_string_field("General.CheckAuths.alertchan"); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) { + putsock(alertclient, "PRIVMSG %s :Deleted User %s", alertchan_chan->name, auth); + } + } + } else if(exists && !strcmp(row[1], "0")) { + printf_mysql_query("UPDATE `users` SET `user_registered` = '%lu', `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", (unsigned long) registered, row[0]); + } else { + printf_mysql_query("UPDATE `users` SET `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", row[0]); + } + } +} + +TIMEQ_CALLBACK(main_checkauths) { + int next_call = 600; + if(get_int_field("General.CheckAuths.enabled")) { + int check_start_time = get_int_field("General.CheckAuths.start_time") * 3600; + int duration = get_int_field("General.CheckAuths.duration") * 60; + int now = getCurrentSecondsOfDay(); + if(now < check_start_time && check_start_time+duration >= 86400) { + check_start_time -= 86400; + } + if(now >= check_start_time && now < (check_start_time + duration)) { + next_call = get_int_field("General.CheckAuths.interval"); + //get the "longest-unchecked-user" + MYSQL_RES *res; + MYSQL_ROW row; + int lastcheck; + time_t unixtime = time(0); + int min_unckecked = get_int_field("General.CheckAuths.min_unckecked"); + printf_mysql_query("SELECT `user_user`, `user_lastcheck` FROM `users` ORDER BY `user_lastcheck` ASC LIMIT 1"); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + lastcheck = atoi(row[1]); + if(!lastcheck || unixtime - lastcheck >= min_unckecked) { + lookup_authname(row[0], main_checkauths_callback, NULL); + } else + next_call = 300; + } + } else { + int pending; + if(now > check_start_time) + pending = 86400 - now + check_start_time; + else + pending = check_start_time - now; + if(pending < 600) + next_call = pending; + } + + } + timeq_add(next_call, main_checkauths, NULL); +} + TIMEQ_CALLBACK(main_statistics) { int update_minutes = get_int_field("statistics.frequency"); if(!update_minutes) update_minutes = 2; diff --git a/src/mysqlConn.c b/src/mysqlConn.c index 5fc14e9..12ab76d 100644 --- a/src/mysqlConn.c +++ b/src/mysqlConn.c @@ -16,7 +16,7 @@ */ #include "mysqlConn.h" -#define DATABASE_VERSION "14" +#define DATABASE_VERSION "15" struct mysql_conn_struct { unsigned long tid; -- 2.20.1