summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2024-03-31 22:17:16 +0100
committerLuke Bratch <luke@bratch.co.uk>2024-03-31 22:17:16 +0100
commit7ac369cbe46739beac37d97642b65b98c46ffc4d (patch)
treea64b89a5e5a13b16c3c4cbc4205b2ef0bcec2a16
parent40447ede689b5ea75f1c1f7bf6345eecd4699b0a (diff)
Handle server KICK commands.
-rw-r--r--logging.c39
-rw-r--r--logging.h4
-rw-r--r--message.c38
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]);