From 93e530320e024e9119fb8717459ef618086c5839 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Thu, 16 May 2019 23:40:43 +0100 Subject: Correctly handle nicks changing and actually track users PARTing channels. Also change nickuserhost to store the leading colon (:) since it's always needed (so far). --- blabouncer.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- functions.c | 32 +++++++++++++++++++ functions.h | 5 +++ 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/blabouncer.c b/blabouncer.c index 7d2ea31..2a3bf7e 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -6,7 +6,7 @@ // - Do we actually need to store the modes in the channel struct? // - Get CAP from server and relay to client // - "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 +// - Keep track of changing user modes // - Should replay log do more than PRIVMSGs? // - Consider moving the three fd-related arrays into one struct // - Check authentication before even getting to the send functions to save unnecessary processing @@ -84,8 +84,8 @@ struct ircdstrings { char greeting003[MAXDATASIZE]; char greeting004[MAXDATASIZE]; char ircdname[MAXDATASIZE]; - char nickuserhost[MAXDATASIZE]; // TODO - Make sure this changes when nick changes, if that's meant to happen. - char ircnick[MAXNICKLENGTH]; // TODO - Make sure this changes when nick changes + char nickuserhost[MAXDATASIZE]; + char ircnick[MAXNICKLENGTH]; char ircusername[MAXUSERNAMELEN]; char currentmsg[MAXDATASIZE]; // Holding area for the current server-received IRC message being processed in case it needs building across multiple reads (i.e. a truncated/split message) }; @@ -328,6 +328,51 @@ int addusertochannel(struct channel *channels, char *channelname, char *nick) { return 0; } +int removeuserfromchannel(struct channel *channels, char *channelname, char *nick) { + printf("removeuserfromchannel(): given \"%s\" and \"%s\".\n", channelname, nick); + + // Go through all the channels + for (int i = 0; i < MAXCHANNELS; i++) { + // Until we find the channel + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + // Go through all the users in the channel + for (int j = 0; j < MAXCHANUSERS; j++) { + // And remove the user if found + if (strncmp(channels[i].nicks[j], nick, strlen(nick)) == 0) { + channels[i].nicks[j][0] = '\0'; + return 1; + } + } + // We failed + return 0; + } + } + + // TODO - Make a failed return do something to callers + return 0; +} + +// Update any instance of oldnick with newnick in all channels, +// for instance of a new changed their nick. +int updatechannelnicks(struct channel *channels, char *oldnick, char *newnick) { + printf("updatechannelnicks(): given '%s' and '%s'.\n", oldnick, newnick); + + // Go through all the channels + for (int i = 0; i < MAXCHANNELS; i++) { + // Go through all the users in the channel + for (int j = 0; j < MAXCHANUSERS; j++) { + // And update the nick if found + if (strncmp(channels[i].nicks[j], oldnick, strlen(oldnick)) == 0) { + printf("Updating '%s' to '%s' in '%s'.\n", channels[i].nicks[j], newnick, channels[i].name); + strcpy(channels[i].nicks[j], newnick); + } + } + } + + // TODO - Detect failing to update anything and tell the caller + return 1; +} + int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when) { printf("setchanneltopicwhotime(): given \"%s\", \"%s\", and \"%s\".\n", channelname, who, when); @@ -536,10 +581,15 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, strncpy(ircdstrings->greeting001, str, strlen(str)); // Null the end of the string ircdstrings->greeting001[strlen(str)] = '\0'; - printf("Storing our nick!user@host (%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]); - strncpy(ircdstrings->nickuserhost, tokens[counter - 1], strlen(tokens[counter - 1])); - // Null the end of the string - ircdstrings->nickuserhost[strlen(tokens[counter - 1])] = '\0'; + printf("Storing our nick!user@host (:%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]); + // Prepend a colon (:) first since everything (so far) needs one + if (!snprintf(ircdstrings->nickuserhost, MAXDATASIZE, ":%s", tokens[counter - 1])) { + fprintf(stderr, "Error while preparing nickuserhost for storage!\n"); + exit(1); + } + // Null the end of the new string + ircdstrings->nickuserhost[strlen(tokens[counter - 1]) + 1] = '\0'; // +1 for the inserted colon + printf("nickuserhost '%s' stored.\n", ircdstrings->nickuserhost); return 1; } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) { printf("Found greeting 002 (%s), storing in ircdstrings struct.\n", str); @@ -576,7 +626,6 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, // If the user JOINing is us, then we must have joined a channel, so add to our local channel array. // Copy to a temporary string so we still have the original in case we need it char *prefixcopy = strdup(tokens[0]); - stripprefix(prefixcopy); if (strncmp(prefixcopy, ircdstrings->nickuserhost, strlen(tokens[0])) == 0) { printf("Server JOIN: nickuserhost is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost); // TODO - Saner way to initialise this since we don't have the variables yet? @@ -603,12 +652,13 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, // (If it's not us, then it's another user PARTing a channel, so just pass straight through to letting all our clients know.) // Copy to a temporary string so we still have the original in case we need it char *prefixcopy = strdup(tokens[0]); - stripprefix(prefixcopy); if (strncmp(prefixcopy, ircdstrings->nickuserhost, strlen(tokens[0])) == 0) { printf("Server PART: nickuserhost is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost); removechannel(channels, tokens[2]); } else { printf("Server PART: nickuserhost is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost); + extractnickfromprefix(tokens[0]); + removeuserfromchannel(channels, tokens[2], tokens[0]); } // And then send to all clients @@ -701,6 +751,36 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, return 1; } + + // Server NICK received? + // 1. Find out if it was us and change ircnick and nickuserhost if so + // 2. Either way, update our records of nicks in channels + // 3. Either way, relay to all clients + if (strncmp(tokens[1], "NICK", strlen(tokens[1])) == 0) { + printf("Server NICK found and it is: %s with length %zd! Next token is '%s'. Updating records and relaying to all clients.\n", tokens[0], strlen(tokens[0]), tokens[2]); + + // Was it us? + if (strncmp(ircdstrings->nickuserhost, tokens[0], strlen(ircdstrings->nickuserhost)) == 0) { + // Update nickuserhost with the new :nick!user@host + updatenickuserhost(ircdstrings->nickuserhost, tokens[2]); + printf("Updated nickuserhost to '%s'.\n", ircdstrings->nickuserhost); + // Update ircnick + char *prefixcopy = strdup(ircdstrings->nickuserhost); + extractnickfromprefix(prefixcopy); + strcpy(ircdstrings->ircnick, prefixcopy); + printf("Updated ircnick to '%s'.\n", ircdstrings->ircnick); + } + + // Update all nicks in channels + extractnickfromprefix(tokens[0]); + stripprefix(tokens[2]); + updatechannelnicks(channels, tokens[0], tokens[2]); + + // Relay to all clients + sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings); + + return 1; + } } // Don't return if we got here because this means we didn't process something above @@ -767,7 +847,7 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, } // Get client to join channels - if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) { + if (!snprintf(outgoingmsg, MAXDATASIZE, "%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) { fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n"); exit(1); } @@ -873,7 +953,7 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, // Rebuild to full PRIVMSG string and relay to all other clients char outgoingmsg[MAXDATASIZE]; // String to send to client - if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s %s", ircdstrings->nickuserhost, str)) { + if (!snprintf(outgoingmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) { fprintf(stderr, "Error while preparing PRIVMSG relay from another bouncer client.\n"); exit(1); } diff --git a/functions.c b/functions.c index e43f31c..6034250 100644 --- a/functions.c +++ b/functions.c @@ -150,3 +150,35 @@ void extractnickfromprefix(char *string) { printf("extractnickfromprefix(): finishing with '%s', strlen: %zd.\n", string, strlen(string)); } + +// Update an existing nickuserhost string with a new nick +void updatenickuserhost(char *nickuserhost, char *nick) { + printf("updatenickuserhost(): updating '%s' with '%s'.\n", nickuserhost, nick); + + // Position of bang + int bangpos = -1; + + // Find the bang + for (size_t i = 0; i < strlen(nickuserhost); i++) { + if (nickuserhost[i] == '!') { + printf("Found bang at position %ld!\n", i); + bangpos = i; + break; + } + } + + if (bangpos == -1) { + printf("No bang found in existing nickuserhost, quitting!\n"); + return; + } + + // Make a new string combining the nick nick and the old nickuserhost + the offset of the bang + char newstr[MAXDATASIZE]; + snprintf(newstr, MAXDATASIZE, "%s%s", nick, nickuserhost + bangpos); + newstr[strlen(nickuserhost) - bangpos + strlen(nick)] = '\0'; + + // Copy back to source string + strcpy(nickuserhost, newstr); + + printf("updatenickuserhost(): new nickuserhost '%s', length '%ld'.\n", nickuserhost, strlen(nickuserhost)); +} diff --git a/functions.h b/functions.h index 4e525eb..de631e9 100644 --- a/functions.h +++ b/functions.h @@ -13,6 +13,8 @@ #include #include +#define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator) + // getstdin() return codes #define OK 0 #define NO_INPUT 1 @@ -33,4 +35,7 @@ void extractfinalparameter(char *string); // Extract the IRC nick from a prefix void extractnickfromprefix(char *string); +// Update an existing nickuserhost string with a new nick +void updatenickuserhost(char *nickuserhost, char *nick); + #endif -- cgit v1.2.3