From e4d1706031540ae74ff104c44adeb735ee0bfcf4 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sat, 7 Sep 2019 12:43:04 +0100 Subject: Add a configuration option to include the date in the timestamp when replaying the replay log. --- README | 1 + TODO | 4 ++-- blabouncer.c | 7 +++++++ blabouncer.conf.example | 3 +++ config.c | 3 +++ functions.c | 11 ++++++++++- replay.c | 42 ++++++++++++++++++++++++++++-------------- replay.h | 3 ++- structures.h | 1 + 9 files changed, 57 insertions(+), 18 deletions(-) diff --git a/README b/README index ff5e5e7..86f2ae4 100644 --- a/README +++ b/README @@ -32,6 +32,7 @@ These options can be changed by issuing a BLABOUNCER REHASH command or by sendin - nick3 - replaymode - replayseconds + - replaydates - password - logging - replaylogging diff --git a/TODO b/TODO index 4e76790..0714ace 100644 --- a/TODO +++ b/TODO @@ -4,8 +4,6 @@ Support arrays or similar in the configuration file (for nick(s), connectcommand All the TODOs sprinkled throughout the code! -Option to include date in replay log replay. - (I think) replay log can cause non-existent user to appear in channel (e.g. ~19:00 on 12/08/2019 for me) Do server operator messages and commands work? @@ -13,3 +11,5 @@ Do server operator messages and commands work? (I vaguely recall) some unwanted stuff (channel ban info?) was relayed to another client upon a client connecting. PM replay chat in a channel (or perhaps a random channel?) e.g. replay on 06/09/2019 at 17:05 from 13:49 in #insomnia - maybe a client thing. + +Ensure replayed lines don't exceed IRC message maximum length due to inserted time/datestamp. diff --git a/blabouncer.c b/blabouncer.c index 17ce41c..7d8dc06 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -935,6 +935,13 @@ int main(int argc, char *argv[]) { exit(1); } + // Should sending replay logs include a datestamp? + settings.replaydates= getconfint("replaydates", settings.conffile); + if (errno == ECONFINT) { + printf("main(): error getting 'replaydates' from configuration file.\n"); + exit(1); + } + // What is the bouncer password? if (!getconfstr("password", settings.conffile, settings.password)) { printf("main(): error getting 'password' from configuration file.\n"); diff --git a/blabouncer.conf.example b/blabouncer.conf.example index 76df607..12727c9 100644 --- a/blabouncer.conf.example +++ b/blabouncer.conf.example @@ -32,6 +32,9 @@ replaymode = "time" # How many seconds of replay log should be sent to connecting clients if replaymode = "time" replayseconds = "600" +# Should replay log timestamps include the date when replaying? ("1" for yes or "0" for no) +replaydates = "0" + # Connect password clients must provided to connect password = "bananas" diff --git a/config.c b/config.c index 18e2178..cea404c 100644 --- a/config.c +++ b/config.c @@ -185,6 +185,9 @@ int createconfigfile(char *filename) { "# How many seconds of replay log should be sent to connecting clients if replaymode = \"time\"\n" "replayseconds = \"600\"\n" "\n" + "# Should replay log timestamps include the date when replaying? (\"1\" for yes or \"0\" for no)\n" + "replaydates = \"0\"\n" + "\n" "# Connect password clients must provided to connect\n" "password = \"bananas\"\n" "\n" diff --git a/functions.c b/functions.c index b9f1080..bbbd9dd 100644 --- a/functions.c +++ b/functions.c @@ -811,7 +811,7 @@ int doreplay(int sourcefd, int replayseconds, struct client *clients, struct set // Replay those lines! for (int i = 0; i < numlines; i++) { - if (!readreplayline(replayseconds, i, outgoingmsg, settings->basedir)) { + if (!readreplayline(replayseconds, i, outgoingmsg, settings->basedir, settings->replaydates)) { debugprint(DEBUG_CRIT, "Error requesting replay line.\n"); return 0; } @@ -1124,6 +1124,15 @@ int rehash(struct settings *settings, char *failuremsg) { return 0; } + // Should sending replay logs include a datestamp? + int oldreplaydates = settings->replaydates; + settings->replaydates = getconfint("replaydates", settings->conffile); + if (errno == ECONFINT) { + settings->replaydates = oldreplaydates; + strcpy(failuremsg, "error getting 'replaydates' from configuration file"); + return 0; + } + // What is the password? char oldpassword[MAXCHAR]; strcpy(oldpassword, settings->password); diff --git a/replay.c b/replay.c index 5e54f28..412ed7c 100644 --- a/replay.c +++ b/replay.c @@ -83,16 +83,21 @@ int striptimestamp(char *str) { // 1557592901 :foo!bar@baz PRIVMSG foo :hello world // And convert it to: // :foo!bar@baz PRIVMSG foo :[17:41:41] hello world +// Or the same but e.g. [DD/MM/YY HH:MM:SS] if replaydates == 1. // Only inserts the formatted time for PRIVMSGs at the moment (and maybe only needs to!). -void formattime(char *str) { +void formattime(char *str, int replaydates) { // Extract the timestamp for conversion into [HH:MM:SS] char timestr[TIMELEN]; sprintf(timestr, "%d", gettimestamp(str)); // Convert int time to string time struct tm tm; strptime(timestr, "%s", &tm); - char timestampf[TIMELEN]; // Formatted timestamp - // Convert into [HH:MM:SS] - strftime(timestampf, TIMELEN, "[%H:%M:%S]", &tm); + char timestampf[DATETIMELEN]; // Formatted timestamp + // Convert into [HH:MM:SS] or e.g. [DD/MM/YY HH:MM:SS] + if (replaydates) { + strftime(timestampf, DATETIMELEN, "[%x %H:%M:%S]", &tm); + } else { + strftime(timestampf, DATETIMELEN, "[%H:%M:%S]", &tm); + } // Strip the original unixtimestamp striptimestamp(str); @@ -144,22 +149,30 @@ void formattime(char *str) { // Second bit (:foo!bar@baz PRIVMSG foo :[HH:MM:SS]) int j = 0; - for (int i = realpos; i < TIMELEN + realpos - 1; i++) { // -1 to avoid the null char from timestampf + for (size_t i = realpos; i < strlen(timestampf) + realpos; i++) { newline[i] = timestampf[j]; j++; } // Insert a space after the formatted timestamp - newline[TIMELEN + realpos - 1] = ' '; + newline[strlen(timestampf) + realpos] = ' '; + newline[strlen(timestampf) + realpos + 1] = '\0'; - // Final bit (the original PRIVMSG contents) - for (int i = TIMELEN + realpos; i < len + TIMELEN; i++) { - newline[i] = str[i - TIMELEN]; + // Record length of temporary newline string and original real message (after the colon) + size_t newlinelen = strlen(newline); + size_t msglen = strlen(str) - realpos - 2; // - 2 to ignore leading colon and null terminator + + // Append the real message to the temporary newline string + for (size_t i = 0; i < strlen(str) - realpos - 2; i++) { + newline[newlinelen + i] = str[realpos + i]; } + // Null terminate it + newline[newlinelen + msglen] = '\0'; + // Copy the whole thing back to str and null terminate - strncpy(str, newline, len + TIMELEN); - str[len + TIMELEN] = '\0'; + strncpy(str, newline, len + strlen(timestampf)); + str[len + strlen(timestampf)] = '\0'; } // Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem. @@ -208,10 +221,11 @@ int replaylines(int seconds, char *basedir) { // Set 'str' to the line in the log with a timestamp of greater than 'seconds' // seconds ago, plus however many lines 'linenum' is set to. 'basedir' is the // directory in which to find 'replay.log'. -// Also modify the line to include a timestamp in the form "[HH:MM:SS]". +// Also modify the line to include a timestamp in the form "[HH:MM:SS]", or [DD/MM/YY HH:MM:SS] +// if replaydates == 1. // Returns 1 on success, or 0 on failure. // TODO - This is horribly inefficient since it re-reads the entire file each call, rewrite this! -int readreplayline(int seconds, int linenum, char *str, char *basedir) { +int readreplayline(int seconds, int linenum, char *str, char *basedir, int replaydates) { FILE *fp; char line[MAXCHAR]; char filename[PATH_MAX]; @@ -246,7 +260,7 @@ int readreplayline(int seconds, int linenum, char *str, char *basedir) { // ...and it is the current requested line then return it if (count == linenum) { // Insert our formatted [HH:MM:SS] timestamp into the message - formattime(line); + formattime(line, replaydates); strncpy(str, line, strlen(line)); str[strlen(line)] = '\0'; diff --git a/replay.h b/replay.h index 5ae9994..c3f23f9 100644 --- a/replay.h +++ b/replay.h @@ -36,10 +36,11 @@ #define MAXCHAR 1000 #define TIMELEN 11 // 32-bit unixtime is up to 10 characters (+1 for null char) // TODO - Make this Year 2038 proof #define TIMELENF 11 // [HH:MM:SS] = 10 characters + 1 for null char +#define DATETIMELEN 50 // Should be enough for a full datetime string // TODO - Is it!? I think it will only ever be as long as [DD/MM/YY HH:MM:SS] (20 including null) int replaylines(int seconds, char *basedir); -int readreplayline(int seconds, int linenum, char *str, char *basedir); +int readreplayline(int seconds, int linenum, char *str, char *basedir, int replaydates); // Returns the number of seconds ago that 'nick' last spoke, or -1 if there is a problem. // 'basedir' is the directory in which to find 'replay.log'. diff --git a/structures.h b/structures.h index a959a92..66c34d0 100644 --- a/structures.h +++ b/structures.h @@ -80,6 +80,7 @@ struct settings { int replaylogging; int debugkeep; int background; // Whether or not we're running in the background (detached from the terminal as a daemon) + int replaydates; // Whether or not to include datestamps when replaying the replay log }; // Structure of a connected clien, their socket/file descriptors, their authentication status, and their OpenSSL structures -- cgit v1.2.3