diff options
-rw-r--r-- | blabouncer.c | 21 | ||||
-rw-r--r-- | logging.c | 151 | ||||
-rw-r--r-- | logging.h | 22 |
3 files changed, 154 insertions, 40 deletions
diff --git a/blabouncer.c b/blabouncer.c index 0d17468..4173914 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -1,8 +1,8 @@ // TODO: -// - Should replay log do more than PRIVMSGs? // - Perhaps rename clients.ssl and server_ssl since they may not even be OpenSSL sockets -// - Is it possible to replay JOINs/PARTs accurately? +// - Is it possible to replay JOINs/PARTs? // - Move debug output into some debug function +// - Log TOPICs // "server" means the real IRC server // "client" means bouncer clients @@ -33,6 +33,8 @@ #define SOURCE_SERVER 0 #define SOURCE_CLIENT 1 #define EXCEPT_NONE 0 +#define LOG_PRIVMSG 0 +#define LOG_JOINPART 1 // It seems to be that *message length* is max 512 bytes, but a socket read from at least UnrealIRCd seems to be up to at least 2416 (+1 for null) bytes. // 1208 bytes with OpenSSL, 2416 bytes with plain text. @@ -465,6 +467,7 @@ int doreplay(int sourcefd, int replayseconds, struct client *clients, struct set // Announce the end snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstrings->ircnick); + sendtoclient(sourcefd, outgoingmsg, clients, settings); return 1; } @@ -662,6 +665,11 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli createchannel(channels, tokens[2], "TOPIC", "TOPICWHO", "0"); } + // Write to normal log if logging enabled + if (settings->logging) { + logline(str, settings->ircnick, settings->basedir, LOG_JOINPART); + } + // And then send to all clients sendtoallclients(clients, str, sourcefd, settings); free(prefixcopy); @@ -682,6 +690,11 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli removechannel(channels, tokens[2]); } + // Write to normal log if logging enabled + if (settings->logging) { + logline(str, settings->ircnick, settings->basedir, LOG_JOINPART); + } + // And then send to all clients sendtoallclients(clients, str, sourcefd, settings); free(prefixcopy); @@ -807,7 +820,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli // Write to normal log if logging enabled if (settings->logging) { - logprivmsg(str, settings->ircnick, settings->basedir); + logline(str, settings->ircnick, settings->basedir, LOG_PRIVMSG); } free(strcopyPtr); @@ -1336,7 +1349,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli // Write to normal log if logging enabled if (settings->logging) { - logprivmsg(outgoingmsg, settings->ircnick, settings->basedir); + logline(outgoingmsg, settings->ircnick, settings->basedir, LOG_PRIVMSG); } free(strcopyPtr); @@ -2,26 +2,46 @@ // 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. +// nick and is used to determine which log file to write to +// if the type is LOG_PRIVMSG. // 'basedir' is the directory in which the 'logs' directory // will be created in which logs are to be written. -// Expects a string in the format: +// +// If LOG_PRIVMSG then it expects a string in the format: // :from!bar@baz PRIVMSG to :hello world -// With the ":foo!bar@baz "prefix being important. +// +// If LOG_JOINPART then +// Expects a string in the format: +// :nick!bar@baz JOIN :#blabouncer +// or +// :nick!bar@baz PART #blabouncer +// +// With the ":foo!bar@baz "prefix being important for either +// type. +// // Returns 1 on success or 0 on failure. -int logprivmsg(char *str, char *ournick, char *basedir) { - // First, extract the "from" nick and the "to" nick or channel by splitting up the string +int logline(char *str, char *ournick, char *basedir, int type) { + // Filename to write to, gets built as we go + char filename[MAXCHAR]; + + // Log line to ultimately write, gets built as we go + char line[MAXCHAR]; - // 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) + // Variables for LOG_JOINPART (can't define directly inside switch case) + int pos; + + // Build array of each space-separated token char tokens[MAXTOKENS][MAXDATASIZE]; char *token; // Split out the first three space-separated parts of the string, leaving the rest. + // If LOG_PRIVMSG: // 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). + // If LOG_JOINPART: + // This gets us the prefix (containing the joined/parted nick), the JOIN/PART command (not needed), + // and the channel name. for (int i = 0; i < 3; i++) { // Try to split if ((token = strsep(&str, " ")) == NULL) { @@ -30,27 +50,81 @@ int logprivmsg(char *str, char *ournick, char *basedir) { } // Copy into the token array (strlen + 1 to get the NULL terminator) strncpy(tokens[i], token, strlen(token) + 1); - counter++; + printf("logline(): extracted '%s'.\n", tokens[i]); } - // 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/logs/%s.log", basedir, tokens[0]); - } else { - // Otherwise log it in the "to" log file - snprintf(filename, MAXCHAR, "%s/logs/%s.log", basedir, tokens[2]); + switch(type) { + case LOG_PRIVMSG: + // Extract the username from the prefix + extractnickfromprefix(tokens[0]); + + // Remove the leading ":" from the real message + stripprefix(str); + + // Build the log filename + // 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/logs/%s.log", basedir, tokens[0]); + } else { + // Otherwise log it in the "to" log file + snprintf(filename, MAXCHAR, "%s/logs/%s.log", basedir, tokens[2]); + } + + printf("logline(): Logging PRIVMSG from '%s' to '%s' message '%s' in filename '%s'.\n", tokens[0], tokens[2], str, filename); + + break; + + case LOG_JOINPART: + // Find the start of the channel name + + // If it's a JOIN + if (tokens[2][0] == ':') { + pos = 1; + } else if (tokens[2][0] == '#') { + // Perhaps it's a PART + pos = 0; + } else { + // If not found, return 0 + return 0; + } + + snprintf(filename, MAXCHAR, "%s/logs/%s.log", basedir, tokens[2] + pos); + + printf("logline(): Logging JOIN/PART to/from '%s' in filename '%s'.\n", tokens[2] + pos, filename); + + // Build a friendly message (e.g. ":nick!user@host JOIN #channel" -> "nick (user@host) has joined #channel") + + // Find the bang in the prefix + char *ret; + int posbang; + if ((ret = strstr(tokens[0], "!")) != NULL) { + // Position within str of "!" + posbang = ret - tokens[0]; + } else { + // No idea what happened, let's abandon ship + return 0; + } + + // Make it a null character + tokens[0][posbang] = '\0'; + + // Swap JOINed or PARTed for a friendly word + if (strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0) { + snprintf(tokens[1], strlen("joined") + 1, "joined"); + } else if (strncmp(tokens[1], "PART", strlen("PART")) == 0) { + snprintf(tokens[1], strlen("left") + 1, "left"); + } + + // Copy the nick, then user@host, then whether it was a join or part, then the channel name the string to send + snprintf(line, MAXCHAR, "%s (%s) has %s %s", tokens[0] + 1, tokens[0] + posbang + 1, tokens[1], tokens[2] + pos); + + break; + + default : + printf("Unknown log type '%d', returning 0.\n", type); + return 0; } - printf("logprivmsg(): Logging from '%s' to '%s' message '%s' in filename '%s'.\n", tokens[0], tokens[2], str, filename); - // Make sure the log directory exists char logdir[PATH_MAX]; snprintf(logdir, PATH_MAX, "%s/logs/", basedir); @@ -60,12 +134,11 @@ int logprivmsg(char *str, char *ournick, char *basedir) { printf("Error creating log directory '%s'.\n", logdir); exit(1); } else { - printf("Created log directory '%s'.\n", logdir); + printf("logline(): log directory '%s'.\n", logdir); } } FILE *fp; - char line[MAXCHAR]; int bytes = 0; @@ -86,13 +159,27 @@ int logprivmsg(char *str, char *ournick, char *basedir) { 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); + if (type == LOG_PRIVMSG) { + // Prepend the time string and "from" nick + if (!snprintf(line, MAXCHAR, "%s <%s> %s", timestr, tokens[0], str)) { + fprintf(stderr, "Error while preparing log string to write!\n"); + exit(1); + } + } else if (type == LOG_JOINPART) { + // Prepend the time string + char line2[MAXCHAR]; + if (!snprintf(line2, MAXCHAR, "%s %s", timestr, line)) { + fprintf(stderr, "Error while preparing log string to write!\n"); + exit(1); + } + // Copy back to line to write + snprintf(line, MAXCHAR, "%s", line2); } - printf("Complete log string to write: '%s', length '%ld'.\n", line, strlen(line)); + // Ensure the line finishes with CRLF + appendcrlf(line); + + printf("logline(): Complete log string to write: '%s', length '%ld'.\n", line, strlen(line)); // Write complete line to file if ((bytes = fprintf(fp, line)) < 0) { @@ -6,6 +6,7 @@ #include <time.h> #include <limits.h> #include <sys/stat.h> +#include <ctype.h> #include "functions.h" @@ -15,16 +16,29 @@ #define SOURCE_SERVER 0 #define SOURCE_CLIENT 1 +#define LOG_PRIVMSG 0 +#define LOG_JOINPART 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. +// nick and is used to determine which log file to write to +// if the type is LOG_PRIVMSG. // 'basedir' is the directory in which the 'logs' directory // will be created in which logs are to be written. -// Expects a string in the format: +// +// If LOG_PRIVMSG then it expects a string in the format: // :from!bar@baz PRIVMSG to :hello world -// With the ":foo!bar@baz "prefix being important. +// +// If LOG_JOINPART then +// Expects a string in the format: +// :nick!bar@baz JOIN :#blabouncer +// or +// :nick!bar@baz PART #blabouncer +// +// With the ":foo!bar@baz "prefix being important for either +// type. +// // Returns 1 on success or 0 on failure. -int logprivmsg(char *str, char *ournick, char *basedir); +int logline(char *str, char *ournick, char *basedir, int type); #endif |