add logging feature for commands of each user https://github.com/HarpyWar/pvpgn/issues/47
This commit is contained in:
parent
3613ad99f0
commit
3f2d2072fe
16 changed files with 580 additions and 6 deletions
|
@ -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 = ""
|
||||
|
||||
|
|
|
@ -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 = ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -605,6 +605,19 @@ Syntax for operator/admin:
|
|||
/lang [code]
|
||||
Set your language to get another translation:
|
||||
|
||||
%log
|
||||
--------------------------------------------------------
|
||||
/log <command> <username> [args]
|
||||
Read <username> log with commands (from the end)
|
||||
--------------------------------------------------------
|
||||
/log r[ead] <username> [startline]
|
||||
Show last lines in user log from [startline]
|
||||
/log f[ind] <username> <substring> [startline]
|
||||
Find <substring> in user log from [startline]
|
||||
|
||||
Example: /log read Joe
|
||||
Example: /log find Joe shutdown
|
||||
|
||||
%quiz
|
||||
--------------------------------------------------------
|
||||
/quiz <command> [option]
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -24,6 +24,9 @@ reports_DATA =
|
|||
chanlogsdir = $(localstatedir)/chanlogs
|
||||
chanlogs_DATA =
|
||||
|
||||
userlogsdir = $(localstatedir)/userlogs
|
||||
chanlogs_DATA =
|
||||
|
||||
charinfodir = $(localstatedir)/charinfo
|
||||
charinfo_DATA =
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
336
src/bnetd/userlog.cpp
Normal file
336
src/bnetd/userlog.cpp
Normal file
|
@ -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 <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#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<std::string> 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<std::string> args = split_command(text, 0);
|
||||
std::string cmd = args[0];
|
||||
|
||||
// find command in defined command list
|
||||
for (std::vector<std::string>::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<long, char*> userlog_read(const char * username, long startline, const char * search_substr)
|
||||
{
|
||||
std::map<long, char*> 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<long, char*> 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<long, char*> lines;
|
||||
|
||||
// split command args
|
||||
std::vector<std::string> 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<long, char*>::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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
48
src/bnetd/userlog.h
Normal file
48
src/bnetd/userlog.h
Normal file
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#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<long, char*> userlog_read(const char * username, long startline, const char * search_substr = NULL);
|
||||
extern std::map<long, char*> 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
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in a new issue