From 7ac369cbe46739beac37d97642b65b98c46ffc4d Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sun, 31 Mar 2024 22:17:16 +0100 Subject: Handle server KICK commands. --- logging.c | 39 ++++++++++++++++++++++++++++++++++++--- logging.h | 4 ++++ message.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/logging.c b/logging.c index d0ad1a5..7673fc2 100644 --- a/logging.c +++ b/logging.c @@ -55,6 +55,9 @@ // If LOG_MODE then it expects a string in the format: // :nick!bar@baz MODE #channel foo bar [foo bar...] // +// If LOG_KICK then it expects a string in the format: +// :kicker!bar@baz KICK #channel kickee :bla bla bla +// // With the ":foo!bar@baz "prefix being important for all // types. // @@ -76,7 +79,8 @@ int logline(char *str, struct ircdstate *ircdstate, char *basedir, int type) { // Whether or not this is a "/me" PRIVMSG so we can log it slightly differently later int meprivmsg = 0; - // Split out the first three space-separated parts of the string, leaving the rest. + // Split out the first three (except for LOG_KICK, see below) 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 @@ -89,8 +93,16 @@ int logline(char *str, struct ircdstate *ircdstate, char *basedir, int type) { // the channel whose topic was set, and the rest of the string intact (which is the new topic). // If LOG_NETWORK: // Don't do this at all since we want to log the whole string. + // If LOG_KICK: + // Split out the first four space-separated parts of the string, leaving the rest. + // This gets us the prefix (containing the kicker nick), the KICK command (not needed), + // the channel name, the kickee, and the rest of the string intact (which is the kick message). + int splitnum = 3; + if (type == LOG_KICK) { + splitnum = 4; + } if (type != LOG_NETWORK) { - for (int i = 0; i < 3; i++) { + for (int i = 0; i < splitnum; i++) { // Try to split if ((token = strsep(&str, " ")) == NULL) { debugprint(DEBUG_CRIT, "Error splitting string for logging, returning!\n"); @@ -313,6 +325,27 @@ int logline(char *str, struct ircdstate *ircdstate, char *basedir, int type) { break; + case LOG_KICK: + // Extract the nick from the prefix + extractnickfromprefix(tokens[0], 1); + + // Strip the prefix from the kick message + stripprefix(str, 1); + + // Build a friendly message (e.g. "kicker has kicked kickee from #channel (kick message)") + if (!snprintf(line, MAXCHAR, "%s has kicked %s from %s (%s)", tokens[0], tokens[3], tokens[2], str)) { + debugprint(DEBUG_CRIT, "logline(): Error while preparing friendly message for kick log message, returning!\n"); + return 0; + } + + // Build the log filename + if (!snprintf(filename, MAXCHAR, "%s/logs/%s.log", basedir, to)) { + debugprint(DEBUG_CRIT, "logline(): Error while preparing log filename for kick, returning!\n"); + return 0; + } + + break; + default : debugprint(DEBUG_CRIT, "logline(): Unknown log type '%d', returning 0.\n", type); return 0; @@ -373,7 +406,7 @@ int logline(char *str, struct ircdstate *ircdstate, char *basedir, int type) { } } } else if (type == LOG_JOINPART || type == LOG_TOPIC || type == LOG_NETWORK || - type == LOG_QUIT || type == LOG_NICK || type == LOG_MODE) { + type == LOG_QUIT || type == LOG_NICK || type == LOG_MODE || type == LOG_KICK) { // Prepend the time string char line2[MAXCHAR]; if (!snprintf(line2, MAXCHAR, "%s %s", timestr, line)) { diff --git a/logging.h b/logging.h index b4db7e4..2c9764f 100644 --- a/logging.h +++ b/logging.h @@ -40,6 +40,7 @@ #define LOG_QUIT 4 #define LOG_NICK 5 #define LOG_MODE 6 +#define LOG_KICK 7 #define DEBUG_CRIT 0 #define DEBUG_SOME 1 #define DEBUG_FULL 2 @@ -82,6 +83,9 @@ // If LOG_MODE then it expects a string in the format: // :nick!bar@baz MODE #channel foo bar [foo bar...] // +// If LOG_KICK then it expects a string in the format: +// :kicker!bar@baz KICK #channel kickee :bla bla bla +// // With the ":foo!bar@baz "prefix being important for all // types. // diff --git a/message.c b/message.c index f9e65a2..9d85f63 100644 --- a/message.c +++ b/message.c @@ -247,6 +247,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } // Remove the PARTing nick from our local channel struct + // TODO - don't bother with this if we did removechannel() above? if (!removenickfromchannel(tokens[0], tokens[2], channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to remove nick from channel struct.\n"); } @@ -268,6 +269,43 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int return 1; } + // Server KICK received? Remove from our local channel list if it's us. + if (strncmp(tokens[1], "KICK", strlen(tokens[1])) == 0 && counter >= 3) { + debugprint(DEBUG_FULL, "Server KICK found and it is: %s with length %zd! Next token is '%s'. Removing from local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]); + + // If the user being KICKed is us, then we must have left a channel, so remove it from our local channel array. + // (If it's not us, then it's another user being KICKed from a channel, so just pass straight through to letting all our clients know.) + if ((strlen(tokens[3]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[3], ircdstate->ircnick, strlen(tokens[3])) == 0)) { + debugprint(DEBUG_FULL, "Server KICK: nick is ours ('%s' vs '%s').\n", tokens[3], ircdstate->ircnick); + removechannel(channels, ircdstate->maxchannelcount, tokens[2]); + } else { + debugprint(DEBUG_FULL, "Server KICK: nick is NOT ours ('%s' vs '%s').\n", tokens[3], ircdstate->ircnick); + } + + // Remove the KICKed nick from our local channel struct + // TODO - removenickfromchannel() first argument expects ":nick!user@host" but we're passing "nick" here + // TODO - don't bother with this if we did removechannel() above? + if (!removenickfromchannel(tokens[3], tokens[2], channels, ircdstate->maxchannelcount)) { + debugprint(DEBUG_CRIT, "Server KICK: Failed to remove nick from channel struct.\n"); + } + + // And then send to all clients + sendtoallclients(clients, str, sourcefd, settings); + + // TODO - Consider whether replay for KICK needs to be special (like TOPIC/JOIN/PART/NICK/QUIT in sanitisereplay()) + // Write to replay log if replay logging enabled + if (settings->replaylogging) { + writereplayline(str, settings->basedir); + } + + // Write to normal log if logging enabled + if (settings->logging) { + logline(str, ircdstate, settings->basedir, LOG_KICK); + } + + return 1; + } + // Server QUIT received? Tell all clients and also remove the user from our local channels struct. if (strncmp(tokens[1], "QUIT", strlen(tokens[1])) == 0) { debugprint(DEBUG_FULL, "Server QUIT found and it is: %s with length %zd! Next token is '%s'.\n", tokens[0], strlen(tokens[0]), tokens[2]); -- cgit v1.2.3