diff --git a/conf/bnetd.conf.in b/conf/bnetd.conf.in index e8f1872..ab8f387 100644 --- a/conf/bnetd.conf.in +++ b/conf/bnetd.conf.in @@ -82,6 +82,7 @@ filedir = "${LOCALSTATEDIR}/files" scriptdir = "${LOCALSTATEDIR}/lua" reportdir = "${LOCALSTATEDIR}/reports" chanlogdir = "${LOCALSTATEDIR}/chanlogs" +userlogdir = "${LOCALSTATEDIR}/userlogs" i18ndir = "${SYSCONFDIR}/i18n" issuefile = "${SYSCONFDIR}/bnissue.txt" channelfile = "${SYSCONFDIR}/channel.conf" @@ -659,3 +660,20 @@ clan_channel_default_private = 0 # without accepted invitations need. This does not affect WAR3/W3XP ingame # clan management. clan_min_invites = 2 + +############################################################################### +# user commands logging # +#-----------------------------------------------------------------------------# + +# Should commands from users be logged to files in the userlogdir? +log_commands = true + +# Commands will be logged for users with these command groups +# (global operators and admins are included) +log_command_groups = 2345678 + +# List of commands that will be logged +# can be defined by connecting them with a comma (,) +# set empty to log all commands +log_command_list = "" + diff --git a/conf/bnetd.conf.win32 b/conf/bnetd.conf.win32 index b6bce9e..78f7cbb 100644 --- a/conf/bnetd.conf.win32 +++ b/conf/bnetd.conf.win32 @@ -62,6 +62,7 @@ filedir = files scriptdir = lua reportdir = var\reports chanlogdir = var\chanlogs +userlogdir = var\userlogs i18ndir = conf\i18n issuefile = conf\bnissue.txt channelfile = conf\channel.conf @@ -640,3 +641,20 @@ clan_channel_default_private = 0 # without accepted invitations need. This does not affect WAR3/W3XP ingame # clan management. clan_min_invites = 2 + +############################################################################### +# user commands logging # +#-----------------------------------------------------------------------------# + +# Should commands from users be logged to files in the userlogdir? +log_commands = true + +# Commands will be logged for users with these command groups +# (global operators and admins are included) +log_command_groups = 2345678 + +# List of commands that will be logged +# can be defined by connecting them with a comma (,) +# set empty to log all commands +log_command_list = "" + diff --git a/conf/command_groups.conf.in b/conf/command_groups.conf.in index 6309d08..f08219f 100644 --- a/conf/command_groups.conf.in +++ b/conf/command_groups.conf.in @@ -165,6 +165,7 @@ # but will allow them to make themselves Server Admin & Server Operator. 6 /operator /admin /flag /tag +6 /log 7 /set 7 /commandgroups /cg diff --git a/conf/i18n/bnhelp.conf b/conf/i18n/bnhelp.conf index a8e8318..297ed11 100644 --- a/conf/i18n/bnhelp.conf +++ b/conf/i18n/bnhelp.conf @@ -605,6 +605,19 @@ Syntax for operator/admin: /lang [code] Set your language to get another translation: +%log +-------------------------------------------------------- +/log [args] + Read log with commands (from the end) +-------------------------------------------------------- + /log r[ead] [startline] + Show last lines in user log from [startline] + /log f[ind] [startline] + Find in user log from [startline] + + Example: /log read Joe + Example: /log find Joe shutdown + %quiz -------------------------------------------------------- /quiz [option] diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 3455467..1a2ea56 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -7,6 +7,7 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/clans") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/teams") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/reports") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/chanlogs") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/userlogs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/charinfo") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/charsave") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/bak/charinfo") diff --git a/files/Makefile.am b/files/Makefile.am index 177753a..36f0816 100644 --- a/files/Makefile.am +++ b/files/Makefile.am @@ -24,6 +24,9 @@ reports_DATA = chanlogsdir = $(localstatedir)/chanlogs chanlogs_DATA = +userlogsdir = $(localstatedir)/userlogs +chanlogs_DATA = + charinfodir = $(localstatedir)/charinfo charinfo_DATA = diff --git a/src/bnetd/CMakeLists.txt b/src/bnetd/CMakeLists.txt index 7007acb..e3ed69a 100644 --- a/src/bnetd/CMakeLists.txt +++ b/src/bnetd/CMakeLists.txt @@ -36,7 +36,7 @@ set(BNETD_SOURCES anongame_wol.cpp anongame_wol.h handle_wserv.cpp handle_wserv.h luafunctions.cpp luafunctions.h luainterface.cpp luainterface.h luaobjects.cpp luaobjects.h luawrapper.cpp luawrapper.h - i18n.cpp i18n.h + i18n.cpp i18n.h cmdlog.cpp cmdlog.h ../win32/winmain.cpp ../win32/winmain.h ) diff --git a/src/bnetd/Makefile.am b/src/bnetd/Makefile.am index b7c06ac..eda84c4 100644 --- a/src/bnetd/Makefile.am +++ b/src/bnetd/Makefile.am @@ -16,7 +16,7 @@ bnetd_SOURCES = account.cpp account_wrap.cpp adbanner.cpp alias_command.cpp anon tournament.cpp tracker.cpp udptest_send.cpp versioncheck.cpp watch.cpp \ storage_sql2.cpp sql_common.cpp handle_wol.cpp handle_irc_common.cpp handle_apireg.cpp \ handle_wserv.cpp luafunctions.cpp luainterface.cpp luaobjects.cpp luawrapper.cpp \ - i18n.cpp icons.cpp + i18n.cpp icons.cpp cmdlog.cpp bnetd_LDADD = $(top_builddir)/src/common/libcommon.a \ $(top_builddir)/src/compat/libcompat.a \ @@ -37,4 +37,4 @@ noinst_HEADERS = account.h account_wrap.h adbanner.h alias_command.h \ timer.h topic.h tournament.h udptest_send.h versioncheck.h watch.h \ tracker.h storage_sql2.h sql_common.h handle_wol.h handle_irc_common.h handle_apireg.h \ handle_wserv.h luafunctions.h luainterface.h luaobjects.h luawrapper.h \ - i18n.h icons.h + i18n.h icons.h cmdlog.h diff --git a/src/bnetd/command.cpp b/src/bnetd/command.cpp index 2af4285..8db1073 100644 --- a/src/bnetd/command.cpp +++ b/src/bnetd/command.cpp @@ -81,6 +81,7 @@ #include "clan.h" #include "common/flags.h" #include "icons.h" +#include "userlog.h" #include "i18n.h" #include "attrlayer.h" @@ -543,6 +544,7 @@ namespace pvpgn { "/alert", _handle_alert_command }, { "/language", handle_language_command }, { "/lang", handle_language_command }, + { "/log", handle_log_command }, { NULL, NULL } @@ -574,7 +576,9 @@ namespace pvpgn // -1 = unsuccess, 0 = success, 1 = execute next c++ code if (result == 0) { - // TODO: log command + // log command + if (t_account * account = conn_get_account(c)) + userlog_append(account, text); } if (result == 0 || result == -1) return result; @@ -600,7 +604,10 @@ namespace pvpgn // -1 = unsuccess, 0 = success if (result == 0) { - // TODO: log command + // log command + if (t_account * account = conn_get_account(c)) + userlog_append(account, text); + // TODO: modify all commands to return "0" only if success, and "-1" if not } return result; diff --git a/src/bnetd/luainterface.cpp b/src/bnetd/luainterface.cpp index 31a7e3c..b1a0824 100644 --- a/src/bnetd/luainterface.cpp +++ b/src/bnetd/luainterface.cpp @@ -177,6 +177,7 @@ namespace pvpgn config.update("scriptdir", prefs_get_scriptdir()); config.update("reportdir", prefs_get_reportdir()); config.update("chanlogdir", prefs_get_chanlogdir()); + config.update("userlogdir", prefs_get_userlogdir()); config.update("localizefile", prefs_get_localizefile()); config.update("motdfile", prefs_get_motdfile()); config.update("motdw3file", prefs_get_motdw3file()); @@ -298,6 +299,10 @@ namespace pvpgn config.update("clan_max_members", prefs_get_clan_max_members()); config.update("clan_channel_default_private", prefs_get_clan_channel_default_private()); config.update("clan_min_invites", prefs_get_clan_min_invites()); + config.update("log_commands", prefs_get_log_commands()); + config.update("log_command_groups", prefs_get_log_command_groups()); + config.update("log_command_list", prefs_get_log_command_list()); + } } diff --git a/src/bnetd/main.cpp b/src/bnetd/main.cpp index b4e9460..36fb5bd 100644 --- a/src/bnetd/main.cpp +++ b/src/bnetd/main.cpp @@ -90,6 +90,7 @@ #include "topic.h" #include "handle_apireg.h" #include "i18n.h" +#include "userlog.h" #include "common/setup_after.h" #ifdef WITH_LUA @@ -384,6 +385,7 @@ int pre_server_startup(void) if (realmlist_create(prefs_get_realmfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list"); topiclist_load(prefs_get_topicfile()); + userlog_init(); #ifdef WITH_LUA lua_load(prefs_get_scriptdir()); diff --git a/src/bnetd/prefs.cpp b/src/bnetd/prefs.cpp index 8f3202b..a42241f 100644 --- a/src/bnetd/prefs.cpp +++ b/src/bnetd/prefs.cpp @@ -100,6 +100,7 @@ namespace pvpgn char const * transfile; unsigned int chanlog; char const * chanlogdir; + char const * userlogdir; unsigned int quota; unsigned int quota_lines; unsigned int quota_time; @@ -164,6 +165,9 @@ namespace pvpgn unsigned int sync_on_logoff; char const * irc_network_name; unsigned int localize_by_country; + unsigned int log_commands; + char const * log_command_groups; + char const * log_command_list; char const * apiregaddrs; char const * wolv1addrs; @@ -417,6 +421,10 @@ namespace pvpgn static const char *conf_get_chanlogdir(void); static int conf_setdef_chanlogdir(void); + static int conf_set_userlogdir(const char *valstr); + static const char *conf_get_userlogdir(void); + static int conf_setdef_userlogdir(void); + static int conf_set_quota(const char *valstr); static const char *conf_get_quota(void); static int conf_setdef_quota(void); @@ -669,6 +677,18 @@ namespace pvpgn static const char *conf_get_localize_by_country(void); static int conf_setdef_localize_by_country(void); + static int conf_set_log_commands(const char *valstr); + static const char *conf_get_log_commands(void); + static int conf_setdef_log_commands(void); + + static int conf_set_log_command_groups(const char *valstr); + static const char *conf_get_log_command_groups(void); + static int conf_setdef_log_command_groups(void); + + static int conf_set_log_command_list(const char *valstr); + static const char *conf_get_log_command_list(void); + static int conf_setdef_log_command_list(void); + static int conf_setdef_apireg_addrs(void); static int conf_set_apireg_addrs(const char *valstr); @@ -773,6 +793,7 @@ namespace pvpgn { "transfile", conf_set_transfile, conf_get_transfile, conf_setdef_transfile }, { "chanlog", conf_set_chanlog, conf_get_chanlog, conf_setdef_chanlog }, { "chanlogdir", conf_set_chanlogdir, conf_get_chanlogdir, conf_setdef_chanlogdir }, + { "userlogdir", conf_set_userlogdir, conf_get_userlogdir, conf_setdef_userlogdir }, { "quota", conf_set_quota, conf_get_quota, conf_setdef_quota }, { "quota_lines", conf_set_quota_lines, conf_get_quota_lines, conf_setdef_quota_lines }, { "quota_time", conf_set_quota_time, conf_get_quota_time, conf_setdef_quota_time }, @@ -836,6 +857,9 @@ namespace pvpgn { "ladder_prefix", conf_set_ladder_prefix, conf_get_ladder_prefix, conf_setdef_ladder_prefix }, { "irc_network_name", conf_set_irc_network_name, conf_get_irc_network_name, conf_setdef_irc_network_name }, { "localize_by_country", conf_set_localize_by_country, conf_get_localize_by_country, conf_setdef_localize_by_country }, + { "log_commands", conf_set_log_commands, conf_get_log_commands, conf_setdef_log_commands }, + { "log_command_groups", conf_set_log_command_groups, conf_get_log_command_groups, conf_setdef_log_command_groups }, + { "log_command_list", conf_set_log_command_list, conf_get_log_command_list, conf_setdef_log_command_list }, { "apiregaddrs", conf_set_apireg_addrs, conf_get_apireg_addrs, conf_setdef_apireg_addrs }, { "wgameresaddrs", conf_set_wgameres_addrs, conf_get_wgameres_addrs, conf_setdef_wgameres_addrs }, @@ -2126,6 +2150,27 @@ namespace pvpgn } + extern char const * prefs_get_userlogdir(void) + { + return prefs_runtime_config.userlogdir; + } + + static int conf_set_userlogdir(const char *valstr) + { + return conf_set_str(&prefs_runtime_config.userlogdir, valstr, NULL); + } + + static int conf_setdef_userlogdir(void) + { + return conf_set_str(&prefs_runtime_config.userlogdir, NULL, BNETD_USERLOG_DIR); + } + + static const char* conf_get_userlogdir(void) + { + return prefs_runtime_config.userlogdir; + } + + extern unsigned int prefs_get_quota(void) { return prefs_runtime_config.quota; @@ -3507,6 +3552,70 @@ namespace pvpgn } + extern unsigned int prefs_get_log_commands(void) + { + return prefs_runtime_config.log_commands; + } + + static int conf_set_log_commands(const char *valstr) + { + return conf_set_bool(&prefs_runtime_config.log_commands, valstr, 0); + } + + static int conf_setdef_log_commands(void) + { + return conf_set_bool(&prefs_runtime_config.log_commands, NULL, 1); + } + + static const char* conf_get_log_commands(void) + { + return conf_get_bool(prefs_runtime_config.log_commands); + } + + + extern char const * prefs_get_log_command_groups(void) + { + return prefs_runtime_config.log_command_groups; + } + + static int conf_set_log_command_groups(const char *valstr) + { + return conf_set_str(&prefs_runtime_config.log_command_groups, valstr, NULL); + } + + static int conf_setdef_log_command_groups(void) + { + return conf_set_str(&prefs_runtime_config.log_command_groups, NULL, BNETD_LOG_COMMAND_GROUPS); + } + + static const char* conf_get_log_command_groups(void) + { + return prefs_runtime_config.log_command_groups; + } + + + extern char const * prefs_get_log_command_list(void) + { + return prefs_runtime_config.log_command_list; + } + + static int conf_set_log_command_list(const char *valstr) + { + return conf_set_str(&prefs_runtime_config.log_command_list, valstr, NULL); + } + + static int conf_setdef_log_command_list(void) + { + return conf_set_str(&prefs_runtime_config.log_command_list, NULL, BNETD_LOG_COMMAND_LIST); + } + + static const char* conf_get_log_command_list(void) + { + return prefs_runtime_config.log_command_list; + } + + + /** * Westwood Online Extensions */ diff --git a/src/bnetd/prefs.h b/src/bnetd/prefs.h index f9dfbdc..bb68dcf 100644 --- a/src/bnetd/prefs.h +++ b/src/bnetd/prefs.h @@ -95,6 +95,7 @@ namespace pvpgn extern char const * prefs_get_transfile(void); extern unsigned int prefs_get_chanlog(void); extern char const * prefs_get_chanlogdir(void); + extern char const * prefs_get_userlogdir(void); extern unsigned int prefs_get_quota(void); extern unsigned int prefs_get_quota_lines(void); extern unsigned int prefs_get_quota_time(void); @@ -180,6 +181,9 @@ namespace pvpgn extern unsigned int prefs_get_sync_on_logoff(void); extern char const * prefs_get_irc_network_name(void); extern unsigned int prefs_get_localize_by_country(void); + extern unsigned int prefs_get_log_commands(void); + extern char const * prefs_get_log_command_groups(void); + extern char const * prefs_get_log_command_list(void); /** * Westwood Online Extensions diff --git a/src/bnetd/userlog.cpp b/src/bnetd/userlog.cpp new file mode 100644 index 0000000..c264340 --- /dev/null +++ b/src/bnetd/userlog.cpp @@ -0,0 +1,336 @@ +/* +* Copyright (C) 2014 HarpyWar (harpywar@gmail.com) +* +* 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 2 +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "common/setup_before.h" + +#include +#include +#include +#include + +#include "compat/strcasecmp.h" +#include "compat/snprintf.h" +#include "compat/pdir.h" +#include "compat/mkdir.h" +#include "common/util.h" +#include "common/eventlog.h" +#include "common/xstring.h" + +#include "connection.h" +#include "message.h" +#include "account.h" +#include "account_wrap.h" +#include "command_groups.h" +#include "command.h" +#include "prefs.h" +#include "helpfile.h" +#include "i18n.h" +#include "userlog.h" + +#include "common/setup_after.h" + + +namespace pvpgn +{ + + namespace bnetd + { + // max output lines to send user from /log command result + static const int userlog_max_output_lines = 50; + + static std::vector userlog_commands; + + extern void userlog_init() + { + char * temp; + char const * tok; + + userlog_commands.clear(); + + // fill command list that must be logged + if (const char * cmdlist = prefs_get_log_command_list()) + { + temp = xstrdup(cmdlist); + tok = std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ + while (tok) { + userlog_commands.push_back(tok); + tok = std::strtok(NULL, ","); + } + xfree(temp); + } + } + + // add new line at the end of log file + extern void userlog_append(t_account * account, const char * text) + { + unsigned int groups = 0; + const char * cglist = prefs_get_log_command_groups(); + + // convert string groups from config to integer + for (int i = 0; i < strlen(cglist); i++) + { + if (cglist[i] == '1') groups |= 1; + else if (cglist[i] == '2') groups |= 2; + else if (cglist[i] == '3') groups |= 4; + else if (cglist[i] == '4') groups |= 8; + else if (cglist[i] == '5') groups |= 16; + else if (cglist[i] == '6') groups |= 32; + else if (cglist[i] == '7') groups |= 64; + else if (cglist[i] == '8') groups |= 128; + } + + // log only commands for admins/operators and users in "groups" defined in config + if (!account_is_operator_or_admin(account, NULL) && !(account_get_command_groups(account) & groups)) + return; + + bool is_found = false; + + // if command list empty then log all commands + if (userlog_commands.size() == 0) + is_found = true; + else + { + // get command name + std::vector args = split_command(text, 0); + std::string cmd = args[0]; + + // find command in defined command list + for (std::vector::iterator it = userlog_commands.begin(); it != userlog_commands.end(); ++it) { + if (*it == cmd) + { + is_found = true; + break; + } + } + } + if (!is_found) + return; + + // get time string + char time_string[USEREVENT_TIME_MAXLEN]; + struct std::tm * tmnow; + std::time_t now; + + std::time(&now); + if (!(tmnow = std::localtime(&now))) + std::strcpy(time_string, "?"); + else + std::strftime(time_string, USEREVENT_TIME_MAXLEN, USEREVENT_TIME_FORMAT, tmnow); + + + char * filename = userlog_filename(account_get_name(account), true); + + if (FILE *fp = fopen(filename, "a")) + { + // append date and text + std::fprintf(fp, "[%s] %s\n", time_string, text); + std::fclose(fp); + } + else + { + ERROR1("could not write into user log file \"%s\"", filename); + } + } + + // read "count" lines from the end starting from "startline" + extern std::map userlog_read(const char * username, long startline, const char * search_substr) + { + std::map lines; + + long linecount = 0; + char line[MAX_MESSAGE_LEN+1]; + int linepos = 0; + + char c, prev_c = 0; + long pos; + + char * filename = userlog_filename(username); + if (FILE *fp = fopen(filename, "r")) + { + // set position to the end of file + fseek(fp, 0, SEEK_END); + pos = ftell(fp); + + // read file reversely by byte + do { + pos--; + fseek(fp, pos, SEEK_SET); + c = fgetc(fp); + + // add char into line array + if (c != '\n') + line[linepos] = c; + + // end of line (or start of file) + if ((c == '\n' && c != prev_c) || pos == -1) + { + // hack for large lines (instead we will receive cut line without start symbols) + if (linepos == MAX_MESSAGE_LEN) + { + // return carriage to read whole line to(from) start + pos = pos + MAX_MESSAGE_LEN; + linepos = 0; + } + if (linepos > 0) + { + line[linepos] = '\0'; // set end of string + strreverse(line); + + linepos = 0; // reset position inside line + + linecount++; + if (linecount >= startline) + { + if (search_substr && strlen(search_substr) > 0) + { + if (find_substr(line, search_substr)) + lines[linecount] = xstrdup(line); + } + else + { + lines[linecount] = xstrdup(line); + } + } + + // limitation of results + if (lines.size() >= userlog_max_output_lines) + break; + } + } + prev_c = c; + if (c != '\n' && linepos < MAX_MESSAGE_LEN) + linepos++; + + } while (c != EOF); + + fclose(fp); + } + return lines; + } + + // search "substring" in user log file from the end starting from "startline" and return "count" results + extern std::map userlog_find_text(const char * username, const char * search_substr, long startline) + { + return userlog_read(username, startline, search_substr); + } + + // return full path for user log filename + // if (force_create_path == true) then create dirs in path if not exist + extern char * userlog_filename(const char * username, bool force_create_path) + { + char * filepath; + + // get first 3 symbols of account and use it in path + // it will improve performance with large count of files + std::string dir_prefix = std::string(username).substr(0, 3); + + filepath = buildpath(prefs_get_userlogdir(), dir_prefix.c_str()); + // create directories in path + if (force_create_path) + { + struct stat statbuf; + // create inside user dir + if (stat(filepath, &statbuf) == -1) { + p_mkdir(filepath, S_IRWXU | S_IRWXG | S_IRWXO); + eventlog(eventlog_level_info, __FUNCTION__, "created user directory: %s", filepath); + } + } + filepath = buildpath(filepath, username); + std::strcat(filepath, ".log"); + + return filepath; + } + + // handle command + // /log read user startline + // /log find user substr startline + extern int handle_log_command(t_connection * c, char const *text) + { + const char *subcommand, *username; + long startline = 0; + std::map lines; + + // split command args + std::vector args = split_command(text, 4); + if (args[1].empty() || args[2].empty() + || (args[1].at(0) != 'r' && args[1].at(0) != 'f')) // check start symbols for subcommand + { + describe_command(c, args[0].c_str()); + return -1; + } + subcommand = args[1].c_str(); // sub command + username = args[2].c_str(); // username + + if (!accountlist_find_account(username)) + { + message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); + return -1; + } + + std::string title = localize(c, "{}'s log output", username); + // read + if (subcommand[0] == 'r') + { + if (!args[3].empty()) + startline = atoi(args[3].c_str()); + + lines = userlog_read(username, startline); + } + // find + else if (subcommand[0] == 'f') + { + if (args[3].empty()) + { + describe_command(c, args[0].c_str()); + return -1; + } + const char * search = args[3].c_str(); + title += localize(c, " by occurrence \"{}\"", search); + + if (!args[4].empty()) + startline = atoi(args[4].c_str()); + + lines = userlog_find_text(username, search, startline); + } + + title += ":"; + message_send_text(c, message_type_info, c, title); + + int linelen = 0; + int paddedlen = 0; + std::string linenum; + + // send each log line to user + for (std::map::reverse_iterator it = lines.rbegin(); it != lines.rend(); ++it) + { + int linelen = floor(log10(abs(it->first))) + 1; // length of integer + if (linelen > paddedlen) + paddedlen = linelen; + + linenum = std_to_string(it->first); + // pad left to max line length + linenum.insert(linenum.begin(), paddedlen - linenum.size(), '0'); + + message_send_text(c, message_type_info, c, linenum + ": " + std::string(it->second)); + } + + return 0; + } + + + } +} diff --git a/src/bnetd/userlog.h b/src/bnetd/userlog.h new file mode 100644 index 0000000..e6a3645 --- /dev/null +++ b/src/bnetd/userlog.h @@ -0,0 +1,48 @@ +/* +* Copyright (C) 2014 HarpyWar (harpywar@gmail.com) +* +* 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 2 +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +/*****/ +#ifndef JUST_NEED_TYPES +#ifndef INCLUDED_USERLOGS_PROTOS +#define INCLUDED_USERLOGS_PROTOS + +#define JUST_NEED_TYPES +#include +#include +#include "connection.h" +#undef JUST_NEED_TYPES + +namespace pvpgn +{ + + namespace bnetd + { + extern void userlog_init(); + + extern void userlog_append(t_account * account, const char * text); + extern std::map userlog_read(const char * username, long startline, const char * search_substr = NULL); + extern std::map userlog_find_text(const char * username, const char * search_substr, long startline); + extern char * userlog_filename(const char * username, bool force_create_path = false); + + extern int handle_log_command(t_connection * c, char const *text); + + } + +} + +#endif +#endif \ No newline at end of file diff --git a/src/common/setup_before.h b/src/common/setup_before.h index 7fae41e..794900e 100644 --- a/src/common/setup_before.h +++ b/src/common/setup_before.h @@ -85,6 +85,10 @@ const int LISTEN_QUEUE = 10; /* the format for game ids */ #define GAMEID_FORMAT "#%06u" +/* the format of timestamps in the userlogfile */ +#define USEREVENT_TIME_FORMAT "%b %d %H:%M" +const int USEREVENT_TIME_MAXLEN = 16; + /* the format of timestamps in the logfile */ #define EVENT_TIME_FORMAT "%b %d %H:%M:%S" const int EVENT_TIME_MAXLEN = 32; @@ -146,7 +150,8 @@ const char * const BNETD_ACCOUNT_TMP = ".bnetd_acct_temp"; const char * const BNETD_IPBAN_FILE = "conf/bnban.conf"; const char * const BNETD_FORTUNECMD = "/usr/games/fortune"; const char * const BNETD_TRANS_FILE = "conf/address_translation.conf"; -const char * const BNETD_CHANLOG_DIR = "chanlogs"; +const char * const BNETD_CHANLOG_DIR = "var/chanlogs"; +const char * const BNETD_USERLOG_DIR = "var/userlogs"; const char * const BNETD_REALM_FILE = "conf/realm.conf"; const char * const BNETD_ISSUE_FILE = "conf/bnissue.txt"; const char * const BNETD_MAIL_DIR = "var/bnmail"; @@ -238,6 +243,10 @@ const unsigned PVPGN_VERSION_TIMEDIV = 0; /* no timediff check by default */ const int PVPGN_CACHE_MEMLIMIT = 5000000; /* bytes */ const char * const PVPGN_DEFAULT_SYMB = "-_[]"; +const char * const BNETD_LOG_COMMAND_GROUPS = "2345678"; +const char * const BNETD_LOG_COMMAND_LIST = ""; + + /***************************************************************/ /* default values for the tracking server */