From a299b62e913df71bdd1c4b41d61d3fb098f12be7 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Thu, 16 May 2019 00:15:47 +0100 Subject: Implement normal logging (file per channel/user). --- Makefile | 6 ++-- blabouncer.c | 10 +++++-- logging.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ logging.h | 26 ++++++++++++++++++ replay.c | 2 +- 5 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 logging.c create mode 100644 logging.h diff --git a/Makefile b/Makefile index f297c74..3316e82 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CC=gcc -DEPS = functions.h sockets.h config.h replay.h +DEPS = functions.h sockets.h config.h replay.h logging.h %.o: %.c $(DEPS) $(CC) -Wall -Wextra -c -o $@ $< -blabouncer: blabouncer.o functions.o sockets.o config.o replay.o - $(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o +blabouncer: blabouncer.o functions.o sockets.o config.o replay.o logging.o + $(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o logging.o diff --git a/blabouncer.c b/blabouncer.c index 2bf2138..3f9004d 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -5,7 +5,6 @@ // - Might need to change channel struct nicks to be channel struct user struct with its own nick/modes/etc. // - Do we actually need to store the modes in the channel struct? // - Get CAP from server and relay to client -// - Add blabouncer MOTD (375, 372, 376) // - "01:53:47 -!- ServerMode/#test [b] by irc.tghost.co.uk" on existing clients when new client connects // - Keep track of changing user nicks/modes // - Should replay log do more than PRIVMSGs? @@ -14,7 +13,7 @@ // - Configurable auto channels // - Comma separated channel list in JOINs/PARTs // - Only send some things to the requesting client (e.g. LIST replies) -// - Normal (non-replay) log +// - Customise logging (disabling it, log file location) // - Alert when clients connect/authenticate/disconnect // - Perhaps rename arr_ssl and server_ssl since they may not even be OpenSSL sockets // @@ -44,6 +43,7 @@ #include "sockets.h" #include "config.h" #include "replay.h" +#include "logging.h" #define SOURCE_SERVER 0 #define SOURCE_CLIENT 1 @@ -688,6 +688,9 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, // Write to replay log writereplayline(str); + // Write to normal log + logprivmsg(str, settings->ircnick); + return 1; } } @@ -872,6 +875,9 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, // Write to replay log writereplayline(outgoingmsg); + // Write to normal log + logprivmsg(outgoingmsg, settings->ircnick); + return 1; } diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..e0f5f1f --- /dev/null +++ b/logging.c @@ -0,0 +1,90 @@ +#include "logging.h" + +// Write the line 'str' to the relevant log file such as +// '#channel.log' or 'nickname.log'. 'ournick' is our own +// nick and is used to determine which log file to write to. +// Expects a string in the format: +// :from!bar@baz PRIVMSG to :hello world +// With the ":foo!bar@baz "prefix being important. +// Returns 1 on success or 0 on failure. +int logprivmsg(char *str, char *ournick) { + // First, extract the "from" nick and the "to" nick or channel by splitting up the string + + // Track which space-separated token within this response we're on + int counter = 0; + // Build array of each space-separated token (TODO - Use counter to stop splitting once we reach some reasonable value - i.e. once we're definitely past commands and into just free text) + char tokens[MAXTOKENS][MAXDATASIZE]; + char *token; + + // Split out the first three space-separated parts of the string, leaving the rest. + // This gets us the prefix (containing the "from" nick), the PRIVMSG command (not needed), + // and the "to" nick or channel. Plus the rest of the string intact (which is the actual + // message). + for (int i = 0; i < 3; i++) { + // Try to split + if ((token = strsep(&str, " ")) == NULL) { + printf("Error splitting string for logging, exiting!\n"); + exit(1); + } + // Copy into the token array (strlen + 1 to get the NULL terminator) + strncpy(tokens[i], token, strlen(token) + 1); + counter++; + } + + // Extract the username from the prefix + extractnickfromprefix(tokens[0]); + + // Remove the leading ":" from the real message + stripprefix(str); + + // Build the log filename + char filename[MAXCHAR]; + // If the message was sent to us, then log it in the sender's log file + if (strncmp(tokens[2], ournick, strlen(tokens[0])) == 0) { + snprintf(filename, MAXCHAR, "%s.log", tokens[0]); + } else { + // Otherwise log it in the "to" log file + snprintf(filename, MAXCHAR, "%s.log", tokens[2]); + } + + printf("logprivmsg(): Logging from '%s' to '%s' message '%s' in filename '%s'.\n", tokens[0], tokens[2], str, filename); + + FILE *fp; + char line[MAXCHAR]; + + int bytes = 0; + + fp = fopen(filename, "a"); + + if (fp == NULL) { + printf("error: could not open log file '%s' for writing.\n", filename); + exit(1); + } + + // Get a current time string to prepend - TODO - Make this customisable. + time_t rawtime; + struct tm * timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + // Strip the trailing newline + char timestr[MAXCHAR]; + snprintf(timestr, MAXCHAR, "%s", asctime(timeinfo)); + timestr[strlen(timestr) - 1] = '\0'; + + // Prepend the time string and "from" nick + if (!snprintf(line, MAXCHAR, "%s <%s> %s", timestr, tokens[0], str)) { + fprintf(stderr, "Error while log string to write!\n"); + exit(1); + } + + printf("Complete log string to write: '%s', length '%ld'.\n", line, strlen(line)); + + // Write complete line to file + if ((bytes = fprintf(fp, line)) < 0) { + printf("error: could not write to log file.\n"); + exit(1); + } + + fclose(fp); + return bytes; +} diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..289f348 --- /dev/null +++ b/logging.h @@ -0,0 +1,26 @@ +#ifndef LOGGING_H_INCLUDED +#define LOGGING_H_INCLUDED + +#include +#include +#include + +#include "functions.h" + +#define MAXTOKENS 100 +#define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator) +#define MAXCHAR 1000 + +#define SOURCE_SERVER 0 +#define SOURCE_CLIENT 1 + +// Write the line 'str' to the relevant log file such as +// '#channel.log' or 'nickname.log'. 'ournick' is our own +// nick and is used to determine which log file to write to. +// Expects a string in the format: +// :from!bar@baz PRIVMSG to :hello world +// With the ":foo!bar@baz "prefix being important. +// Returns 1 on success or 0 on failure. +int logprivmsg(char *str, char *ournick); + +#endif diff --git a/replay.c b/replay.c index 1563c17..3c2c920 100644 --- a/replay.c +++ b/replay.c @@ -228,7 +228,7 @@ int readreplayline(int seconds, int linenum, char *str) { // Write the line 'str' to the replay log file after prepending it with // the current unixtime timestamp. // Expects a string in the format: -// :foo!bar@baz PRIVMSG foo :[17:41:41] hello world +// :from!bar@baz PRIVMSG to :hello world // With the ":foo!bar@baz "prefix being important. // Returns 1 on success or 0 on failure. int writereplayline(char *str) { -- cgit v1.2.3