From 22e93090214af96a5d6d4f83f56c3c7b3a606af5 Mon Sep 17 00:00:00 2001 From: Ari Johnson Date: Mon, 19 Jan 2015 12:22:46 -0500 Subject: [PATCH] New feature: channel logs --- .gitignore | 1 + game/log/chat/README | 2 + game/mushcnf.dst | 3 ++ hdrs/conf.h | 2 + hdrs/extchat.h | 4 ++ src/conf.c | 4 ++ src/extchat.c | 108 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 game/log/chat/README diff --git a/.gitignore b/.gitignore index f19caa3..429ffdc 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ game/data/indb.* game/data/PANIC.db game/save/* game/log/*.log +game/log/chat/*.log game/txt/compose.sh game/txt/connect.txt game/txt/events.txt diff --git a/game/log/chat/README b/game/log/chat/README new file mode 100644 index 0000000..fa0291f --- /dev/null +++ b/game/log/chat/README @@ -0,0 +1,2 @@ +This is the default location for chat logs. Files here are named according to +the channel name with .log appended. diff --git a/game/mushcnf.dst b/game/mushcnf.dst index 39801bb..9681461 100644 --- a/game/mushcnf.dst +++ b/game/mushcnf.dst @@ -535,6 +535,9 @@ log_commands no # log forces done by wizards log_forces yes +# Directory to store chat logs +chatlog_dir log/chat/ + ### ### Logins ### diff --git a/hdrs/conf.h b/hdrs/conf.h index feca962..31220eb 100644 --- a/hdrs/conf.h +++ b/hdrs/conf.h @@ -306,6 +306,7 @@ struct options_table { char command_log[256]; /**< File to log suspect commands */ char trace_log[256]; /**< File to log trace data */ char checkpt_log[256]; /**< File to log checkpoint data */ + char chatlog_dir[256]; /**< Directory to store chat logs */ #ifdef HAS_MYSQL char sql_platform[256]; /**< Type of SQL server, or "disabled" */ char sql_host[256]; /**< Hostname of sql server */ @@ -475,6 +476,7 @@ int cf_time(const char *opt, const char *val, void *loc, int maxval, #define CMDLOG (options.command_log) #define TRACELOG (options.trace_log) #define CHECKLOG (options.checkpt_log) +#define CHATLOGDIR (options.chatlog_dir) #ifdef HAS_MYSQL #define SQL_PLATFORM (options.sql_platform) #define SQL_HOST (options.sql_host) diff --git a/hdrs/extchat.h b/hdrs/extchat.h index 6e09fe6..b005bb2 100644 --- a/hdrs/extchat.h +++ b/hdrs/extchat.h @@ -103,6 +103,7 @@ struct channel { boolexp hidelock; /**< Who may hide from view */ struct channel *next; /**< Next channel in linked list */ BUFFERQ *bufferq; /**< Pointer to channel recall buffer queue */ + FILE *logfile; /**< File to log channel in */ }; /** A list of channels on an object. @@ -138,6 +139,7 @@ struct na_cpass { #define CHANNEL_NOCEMIT 0x400 /* Disallow @cemit */ #define CHANNEL_COBJ 0x800 /* Channel with a channel object */ #define CHANNEL_INTERACT 0x1000 /* Filter channel output through interactions */ +#define CHANNEL_LOG 0x2000 /* Log the channel to a file */ #define CHANNEL_DEFAULT_FLAGS (CHANNEL_PLAYER) #define CL_JOIN 0x1 #define CL_SPEAK 0x2 @@ -168,6 +170,7 @@ int ChanObjCheck _((CHAN *c)); #define ChanSeeLock(c) ((c)->seelock) #define ChanHideLock(c) ((c)->hidelock) #define ChanBufferQ(c) ((c)->bufferq) +#define ChanLogFile(c) ((c)->logfile) #define Channel_Quiet(c) (ChanType(c) & CHANNEL_QUIET) #define Channel_Open(c) (ChanType(c) & CHANNEL_OPEN) #define Channel_Object(c) (ChanType(c) & CHANNEL_OBJECT) @@ -180,6 +183,7 @@ int ChanObjCheck _((CHAN *c)); #define Channel_NoNames(c) (ChanType(c) & CHANNEL_NONAMES) #define Channel_NoCemit(c) (ChanType(c) & CHANNEL_NOCEMIT) #define Channel_Interact(c) (ChanType(c) & CHANNEL_INTERACT) +#define Channel_Log(c) (ChanType(c) & CHANNEL_LOG) #define Chan_Ok_Type(c,o) \ ((ChanObj(c) == o) || (IsPlayer(o) && Channel_Player(c)) || \ (IsThing(o) && Channel_Object(c))) diff --git a/src/conf.c b/src/conf.c index fa65768..0e71f63 100644 --- a/src/conf.c +++ b/src/conf.c @@ -376,6 +376,9 @@ COBRA_CONF conftable[] = { {"connect_log", cf_str, options.connect_log, sizeof options.connect_log, 0, "log"} , + {"chatlog_dir", cf_str, options.chatlog_dir, sizeof options.chatlog_dir, 0, + "log"} + , {"player_flags", cf_flag, options.player_flags, sizeof options.player_flags, 0, "flags"} @@ -1122,6 +1125,7 @@ conf_default_set(void) strcpy(options.trace_log, ""); strcpy(options.wizard_log, ""); strcpy(options.checkpt_log, ""); + strcpy(options.chatlog_dir, ""); options.log_commands = 0; options.log_forces = 1; options.support_pueblo = 0; diff --git a/src/extchat.c b/src/extchat.c index 7fba5d2..edec670 100644 --- a/src/extchat.c +++ b/src/extchat.c @@ -15,6 +15,8 @@ #include #endif #include +#include +#include #include "conf.h" #include "externs.h" #include "attrib.h" @@ -103,6 +105,9 @@ static void format_channel_broadcast(CHAN *chan, CHANUSER *u, dbref victim, const char *extra); static void list_partial_matches(dbref player, const char *name, enum chan_match_type type); +static void begin_chat_log(CHAN *chan); +static void end_chat_log(CHAN *chan); +static void write_chat_log(CHAN *chan, char *buf); const char *chan_speak_lock = "ChanSpeakLock"; /**< Name of speak lock */ const char *chan_join_lock = "ChanJoinLock"; /**< Name of join lock */ @@ -140,6 +145,7 @@ static PRIV priv_table[] = { {"NoCemit", 'C', CHANNEL_NOCEMIT, CHANNEL_NOCEMIT}, {"Interact", 'I', CHANNEL_INTERACT, CHANNEL_INTERACT}, {"ChanObj", 'Z', CHANNEL_COBJ, CHANNEL_COBJ}, + {"LogChannel", 'L', CHANNEL_LOG, CHANNEL_LOG}, {NULL, '\0', 0, 0} }; @@ -232,6 +238,8 @@ load_chatdb_oldstyle(FILE * fp) return 0; } insert_channel(&ch); + if(Channel_Log(ch)) + begin_chat_log(ch); } num_channels = i; @@ -305,6 +313,8 @@ load_chatdb(FILE * fp) return 0; } insert_channel(&ch); + if(Channel_Log(ch)) + begin_chat_log(ch); } num_channels = i; @@ -346,6 +356,7 @@ new_channel(void) ChanMaxUsers(ch) = 0; ChanUsers(ch) = NULL; ChanBufferQ(ch) = NULL; + ch->logfile = NULL; return ch; } @@ -373,6 +384,8 @@ free_channel(CHAN *c) CHANUSER *u, *unext; if (!c) return; + if(c->logfile) + fclose(c->logfile); free_boolexp(ChanJoinLock(c)); free_boolexp(ChanSpeakLock(c)); free_boolexp(ChanHideLock(c)); @@ -1867,6 +1880,8 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) ChanCreator(chan) = Owner(player); strcpy(ChanName(chan), name); insert_channel(&chan); + if(Channel_Log(chan)) + begin_chat_log(chan); notify_format(player, T("CHAT: Channel <%s> created."), ChanName(chan)); break; case 1: @@ -1876,6 +1891,8 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) notify(player, T("Permission denied.")); return; } + if(Channel_Log(chan)) + end_chat_log(chan); /* remove everyone from the channel */ channel_wipe(player, chan); /* refund the owner's money */ @@ -1909,9 +1926,13 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) } /* When we rename a channel, we actually remove it and re-insert it */ strcpy(old, ChanName(chan)); + if(Channel_Log(chan)) + end_chat_log(chan); remove_channel(chan); strcpy(ChanName(chan), perms); insert_channel(&chan); + if(Channel_Log(chan)) + begin_chat_log(chan); channel_broadcast(chan, player, 0, "<%s> %s has renamed channel %s to %s.", ChanName(chan), Name(player), old, ChanName(chan)); @@ -1938,6 +1959,10 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) ("Invalid or same permissions on channel <%s>. No changes made."), ChanName(chan)); } else { + if(Channel_Log(chan) && !(type & CHANNEL_LOG)) + end_chat_log(chan); + else if(!Channel_Log(chan) && (type & CHANNEL_LOG)) + begin_chat_log(chan); ChanType(chan) = type; notify_format(player, T("Permissions on channel <%s> changed."), ChanName(chan)); @@ -3490,6 +3515,8 @@ channel_broadcast(CHAN *channel, dbref player, int flags, const char *fmt, ...) if (ChanBufferQ(channel)) add_to_bufferq(ChanBufferQ(channel), 0, (flags & CB_NOSPOOF) ? player : NOTHING, tbuf1); + if(Channel_Log(channel)) + write_chat_log(channel, tbuf1); } @@ -3692,6 +3719,87 @@ format_channel_broadcast(CHAN *chan, CHANUSER *u, dbref victim, int flags, channel_broadcast(chan, victim, flags, msg, ChanObjName(chan), Name(victim)); } +static void +begin_chat_log(CHAN *chan) +{ + struct stat st; + char filename[512]; + int i, j; + + if(!*CHATLOGDIR) + return; + + if(stat(CHATLOGDIR, &st)) { + do_rawlog(LT_ERR, T("Unable to stat() chatlog_dir: %s"), strerror(errno)); + return; + } + + if(!S_ISDIR(st.st_mode)) { + do_rawlog(LT_ERR, T("chatlog_dir is not a directory")); + return; + } + + strcpy(filename, CHATLOGDIR); /* N.B.: CHATLOGDIR is at most 256 bytes */ + i = strlen(filename); + if(filename[i-1] != '/') { + filename[i] = '/'; + i++; + } + + j = strlen(ChanName(chan)); + if((i + j) > 507) { + do_rawlog(LT_ERR, + T("Buffer length exceeded for chat log filename for channel %s"), + ChanName(chan)); + return; + } + + strcpy(filename + i, ChanName(chan)); + i += j; + + strcpy(filename + i, ".log"); + filename[511] = '\0'; + + ChanLogFile(chan) = fopen(filename, "a"); + if(!ChanLogFile(chan)) { + do_rawlog(LT_ERR, T("Unable to open log file for channel %s: %s"), + ChanName(chan), strerror(errno)); + return; + } + + write_chat_log(chan, "Beginning of log."); +} + +static void +end_chat_log(CHAN *chan) +{ + if(!ChanLogFile(chan)) + return; + + write_chat_log(chan, "End of log."); + fclose(ChanLogFile(chan)); + ChanLogFile(chan) = NULL; +} + +static void +write_chat_log(CHAN *chan, char *buf) +{ + char tbuf[BUFFER_LEN]; + struct tm *when; + char *clean; + + if(!ChanLogFile(chan)) + return; + + when = localtime(&mudtime); + strftime(tbuf, sizeof tbuf, "[%m/%d %H:%M:%S] ", when); + fputs(tbuf, ChanLogFile(chan)); + + clean = remove_markup(buf, NULL); + fputs(clean, ChanLogFile(chan)); + fputc('\n', ChanLogFile(chan)); + fflush(ChanLogFile(chan)); +} static void do_reset_cobj(player, name) -- 2.30.2