From e9d4ad3c33b81ff56c4e4b2cac4aad559a303104 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sun, 15 Sep 2019 14:47:44 +0100 Subject: Start tracking nicks in channels (upon JOIN/PART/QUIT/NICK) and use that to correctly log QUITs in the replay log and normal log(s). --- functions.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 1 deletion(-) (limited to 'functions.c') diff --git a/functions.c b/functions.c index fe96e0a..3dd8045 100644 --- a/functions.c +++ b/functions.c @@ -179,6 +179,7 @@ void appendcrlf(char *string) { } // Remove leading colon ':' which is the starting character of a prefix in an IRC message +// If no leading colon present, string is left unchanged void stripprefix(char *string) { // Make a copy to work with char string2[strlen(string)]; @@ -643,8 +644,11 @@ int createchannel(struct channel *channels, char *name, char *topic, char *topic strncpy(channels[i].topicwhen, topicwhen, strlen(topicwhen)); channels[i].topicwhen[strlen(topicwhen)] = '\0'; channels[i].gotnames = 0; + // Set nicks to blank + for (int j = 0; j < MAXCHANNICKS; j++) { + channels[i].nicks[j][0] = '\0'; + } return 1; - break; // TODO - This should be safe to remove since return is hit first } } @@ -714,6 +718,10 @@ int removechannel(struct channel *channels, char *name) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { // ..and NULL its name (0th character = '\0') channels[i].name[0] = '\0'; + // Set nicks to blank + for (int j = 0; j < MAXCHANNICKS; j++) { + channels[i].nicks[j][0] = '\0'; + } debugprint(DEBUG_FULL, "removechannel(): channel '%s' removed and topicwhen set to '%s'.\n", name, channels[i].topicwhen); return 1; } @@ -1319,3 +1327,243 @@ void replacechar(char *str, char find, char replace) { } } } + +// Add nick (passed as a :nick!user@host) to channel 'channel' +// Returns 1 on success or 0 on failure +int addnicktochannel(char *nickuserhost, char *channel, struct channel *channels) { + debugprint(DEBUG_FULL, "addnicktochannel(): given '%s' and '%s'.\n", nickuserhost, channel); + + // Get the nick from the prefix + extractnickfromprefix(nickuserhost); + + // Make sure the channel exists + int chanfound = 0; + int chanindex; + for (chanindex = 0; chanindex < MAXCHANNELS; chanindex++) { + if (strlen(channels[chanindex].name) == strlen(channel) && !strcmp(channels[chanindex].name, channel)) { + chanfound = 1; + break; + } + } + if (!chanfound) { + debugprint(DEBUG_CRIT, "addnicktochannel(): channel '%s' not found in channel struct.\n", channel); + return 0; + } + + // Add the nick to the channel + for (int i = 0; i < MAXCHANNICKS; i++) { + // Make sure the nick isn't already in the channel struct + if (strlen(channels[chanindex].nicks[i]) == strlen(nickuserhost) && !strcmp(channels[chanindex].nicks[i], nickuserhost)) { + // Unexectedly the nick is already here, hopefully it's OK so let's return 1 + debugprint(DEBUG_FULL, "addnicktochannel(): nick '%s' already in channel '%s', returning.\n", nickuserhost, channel); + return 1; + } + + // Find the first unoccupied slot and put the nick in + if (!channels[chanindex].nicks[i][0]) { + strcpy(channels[chanindex].nicks[i], nickuserhost); + debugprint(DEBUG_FULL, "addnicktochannel(): added nick '%s' to channel '%s'.\n", nickuserhost, channel); + return 1; + } + } + + // We shouldn't get here, return error + debugprint(DEBUG_CRIT, "addnicktochannel(): got to the end of the function without adding nick '%s' to channel '%s', returning error.\n", + nickuserhost, channel); + return 0; +} + +// Remove nick (passed as a :nick!user@host) from channel 'channel' +// Returns 1 on success or 0 on failure +int removenickfromchannel(char *nickuserhost, char *channel, struct channel *channels) { + debugprint(DEBUG_FULL, "removenickfromchannel(): given '%s' and '%s'.\n", nickuserhost, channel); + + // Get the username from the prefix + extractnickfromprefix(nickuserhost); + + // Make sure the channel exists + int chanfound = 0; + int chanindex; + for (chanindex = 0; chanindex < MAXCHANNELS; chanindex++) { + if (strlen(channels[chanindex].name) == strlen(channel) && !strcmp(channels[chanindex].name, channel)) { + chanfound = 1; + break; + } + } + if (!chanfound) { + debugprint(DEBUG_CRIT, "removenickfromchannel(): channel '%s' not found in channel struct.\n", channel); + return 0; + } + + // Remove the nick from the channel + for (int i = 0; i < MAXCHANNICKS; i++) { + // Remove the the nick + if (strlen(channels[chanindex].nicks[i]) == strlen(nickuserhost) && !strcmp(channels[chanindex].nicks[i], nickuserhost)) { + // By null terminating its string + debugprint(DEBUG_FULL, "removenickfromchannel(): nick '%s' removed from channel '%s'.\n", nickuserhost, channel); + channels[chanindex].nicks[i][0] = '\0'; + return 1; + } + } + + // We shouldn't get here, return error + debugprint(DEBUG_CRIT, "removenickfromchannel(): got to the end of the function without removing nick '%s' from channel '%s', returning error.\n", + nickuserhost, channel); + return 0; +} + +// Remove nick (passed as a :nick!user@host) from all channels +// Returns 1 on success or 0 on failure +int removenickfromallchannels(char *nickuserhost, struct channel *channels) { + debugprint(DEBUG_FULL, "removenickfromallchannels(): given '%s'.\n", nickuserhost); + + // Get the nick from the prefix + extractnickfromprefix(nickuserhost); + + // Go through all channels and remove nick if present + for (int i = 0; i < MAXCHANNELS; i++) { + // Go through all nicks in channel + for (int j = 0; j < MAXCHANNICKS; j++) { + // Remove the nick from the channel if present + if (strlen(channels[i].nicks[j]) == strlen(nickuserhost) && !strcmp(channels[i].nicks[j], nickuserhost)) { + // By null terminating its string + channels[i].nicks[j][0] = '\0'; + debugprint(DEBUG_FULL, "removenickfromallchannels(): nick '%s' removed from channel '%s'.\n", nickuserhost, channels[i].name); + } + } + } + + return 1; +} + +// Update old nick (passed as a :nick!user@host) to 'newnick' in all channels +// Returns 1 on success or 0 on failure +int updatenickinallchannels(char *nickuserhost, char *newnick, struct channel *channels) { + debugprint(DEBUG_FULL, "updatenickinallchannels(): given '%s' and '%s'.\n", nickuserhost, newnick); + + // Get the nick from the prefix + extractnickfromprefix(nickuserhost); + + // Strip prefix from newnick + stripprefix(newnick); + + // Go through all channels and update nick if present + for (int i = 0; i < MAXCHANNELS; i++) { + // Go through all nicks in channel + for (int j = 0; j < MAXCHANNICKS; j++) { + // Update the nick in the channel if present + if (strlen(channels[i].nicks[j]) == strlen(nickuserhost) && !strcmp(channels[i].nicks[j], nickuserhost)) { + strcpy(channels[i].nicks[j], newnick); + debugprint(DEBUG_FULL, "updatenickinallchannels(): nick '%s' updated to '%s' in channel '%s'.\n", nickuserhost, newnick, channels[i].name); + } + } + } + + return 1; +} + +// Populate our channels struct with all nicks in a RPL_NAMREPLY +// Returns 1 on success or 0 on failure +int addnamereplytochannel(char *namereply, struct channel *channels) { +//:irc.tghost.co.uk 353 blabounce = #blabouncer :blabounce bbnick ~@l_bratch @l_blabnc Hughbla Bratchbot ars + debugprint(DEBUG_FULL, "addnamereplytochannel(): given '%s'.\n", namereply); + + // Make a copy since we don't need to modify the original + char strcopy[MAXDATASIZE]; + strcpy(strcopy, namereply); + + // Strip the leading ':' + stripprefix(strcopy); + + // Find the start of the channel name, which comes after the first '=' followed by a space + int channelpos = -1; + for (size_t i = 0; i < strlen(strcopy) - 2; i++) { + if (strcopy[i] == '=' && strcopy[i + 1] == ' ' && strcopy[i + 2] != '\0') { + // Name found + channelpos = i + 2; + break; + } + } + if (channelpos == -1) { + // Didn't find the name, abort + debugprint(DEBUG_FULL, "addnamereplytochannel(): couldn't find start of channel name in '%s'.\n", namereply); + return 0; + } + + // Find the end of the channel name + char channelname[MAXCHANLENGTH]; + for (size_t i = channelpos; i < strlen(strcopy); i++) { + // Stop when a space is found or if we're going to exceed MAXCHANLENGTH + if (strcopy[i] == ' ' || i - channelpos == MAXCHANLENGTH - 2) { + break; + } + channelname[i - channelpos] = strcopy[i]; + channelname[i - channelpos + 1] = '\0'; + } + + // Start with a nice clean string that just consists of nicks at the end of the string + char nickstr[MAXDATASIZE]; + strcpy(nickstr, strcopy + channelpos + strlen(channelname) + 1); + + // Split nickstr up into its space-separated nick components + + // Copy to a temporary string for feeding to strsep + char *nickcopy = strdup(nickstr); + // Keep track of initial pointer for free()ing later + char *nickcopyPtr = nickcopy; + + // Track which CLRF-separated nick we're on + int nickcount = 0; + // Build array of each space-separated token + char nicks[MAXTOKENS][MAXDATASIZE]; + // Split the string by ' ' and add each space-separated nick to an array + char *token; + while ((token = strsep(&nickcopy, " ")) != NULL) { + if (*token == '\0') continue; // Skip consecutive matches + if (nickcount >= MAXTOKENS) break; // Too many tokens + debugprint(DEBUG_FULL, "addnamereplytochannel(): Token: '%s', length '%ld'.\n", token, strlen(token)); + // Make sure it's not too long + if (strlen(token) > MAXNICKLENGTH - 1) { + debugprint(DEBUG_CRIT, "addnamereplytochannel(): nick too long, discarding.\n"); + continue; + } + // Copy into the token array (strlen + 1 to get the NULL terminator) + strncpy(nicks[nickcount], token, strlen(token) + 1); + nickcount++; + } + + free(nickcopyPtr); + + // Clean up each nick (remove prefixes and such) + for (int i = 0; i < nickcount; i++) { + stripprefixesfromnick(nicks[i]); + // And add to the channel + addnicktochannel(nicks[i], channelname, channels); + } + + return 1; +} + +// Strips all leading prefixes (colons, user modes) from a nick +void stripprefixesfromnick(char *nick) { + debugprint(DEBUG_FULL, "stripprefixesfromnick(): given '%s'.\n", nick); + + char nicktmp[MAXNICKLENGTH]; + int pos = 0; + + for (size_t i = 0; i < strlen(nick); i++) { + // Only copy non-prefix chars + if (nick[i] != ':' && nick[i] != '~' && nick[i] != '&' && nick[i] != '@' && nick[i] != '%' && nick[i] != '+') { + nicktmp[pos] = nick[i]; + pos++; + } + } + + // Null terminate + nicktmp[pos] = '\0'; + + debugprint(DEBUG_FULL, "stripprefixesfromnick(): produced '%s'.\n", nicktmp); + + // Copy back to source string + strcpy(nick, nicktmp); +} -- cgit v1.2.3