From 0883de2c491a8c79aec13d25bab429aea4362a10 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sun, 22 Dec 2019 20:16:57 +0000 Subject: Significantly reduce memory usage by only initialising channel struct elements when they are used for the first time. --- TODO | 6 ++- blabouncer.c | 21 ++++------ functions.c | 126 +++++++++++++++++++++++++++++++++++------------------------ functions.h | 26 ++++++------ message.c | 56 ++++++++++++-------------- structures.h | 1 + 6 files changed, 126 insertions(+), 110 deletions(-) diff --git a/TODO b/TODO index 4e60cfd..d302400 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,6 @@ macOS compiler may need limits.h included in structures.h. NAMES don't seem to be sent to clients (at least old irssi) after weird timeouts like slow QUIT processing. -Memory usage seems very high. Maybe it's timeouts/reconnecting? Does it only happen with background mode? - When blabouncer reconnects to a server, clients (at least XChat) get stuck with just e.g. [13:14:50]* PONG LAG1574032890090568 [13:15:20]* PONG LAG1574032920149401 @@ -22,3 +20,7 @@ When blabouncer reconnects to a server, clients (at least XChat) get stuck with In channels with no nicks. Add BLABOUNCER VERSION. + +Sometimes replaymode = "lastspoke" will replay the last message you sent if you spoke last and sometimes it doesn't - change to always include your last message? + +Can memory usage be reduced further? (e.g. better channel struct management) diff --git a/blabouncer.c b/blabouncer.c index a9eab97..5ee48b5 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -136,6 +136,7 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, // ircdstate.reconnecting is not set here as we want to track reconnections separately // ircdstate.clientchangetime and ircdstate.clientsnonetime not set here as they are set at startup and only changed when clients connect/disconnect // ircdstate.clientcodes not set here, set on startup and whenever a client sets one + // ircdstate->maxchannelcount not set here, set on startup in dochat() // Populate nicks[0] and username from our configuration file for now, real IRCd may change them later (TODO - Is this true of username?) strcpy(ircdstate->ircnick, settings->ircnicks[0]); @@ -435,14 +436,9 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Struct of channels we're in struct channel *channels; channels = malloc(sizeof(struct channel) * MAXCHANNELS); - // Set initial channel names to empty strings - for (int i = 0; i < MAXCHANNELS; i++) { - channels[i].name[0] = '\0'; - // And all the nicks within it - for (int j = 0; j < MAXCHANNICKS; j++) { - channels[i].nicks[j][0] = '\0'; - } - } + ircdstate.maxchannelcount = 0; + // Each channel struct element is only initialised upon channel creation to avoid consuming lots of memory here. + // The memory is never released once a channel is parted - TODO - Can this channel struct memory usage be improved? // Initialise OpenSSL (used for both client and server) init_openssl(); @@ -710,14 +706,13 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { if (strncmp(outgoingmsg, "listchannels", strlen("listchannels")) == 0) { printf("STDIN command starting: listchannels\n"); - int channelcount = getchannelcount(channels); + int channelcount = getchannelcount(channels, ircdstate.maxchannelcount); - for (int i = 0; i < channelcount; i++) { + for (int i = 0; i < ircdstate.maxchannelcount; i++) { printf("Checking channel[%d] out of %d.\n", i, channelcount); - // Skip this one and increment channelcount if it's a blank channel + // Skip this one if it's a blank channel if (!channels[i].name[0]) { - debugprint(DEBUG_FULL, "Skipping channel[%d], incrementing channelcount.\n", i); - channelcount++; + debugprint(DEBUG_FULL, "Skipping blank channel channel[%d].\n", i); continue; } diff --git a/functions.c b/functions.c index 28c6691..54e7390 100644 --- a/functions.c +++ b/functions.c @@ -632,12 +632,12 @@ int disconnectclient(int fd, struct client *clients, struct ircdstate *ircdstate return 0; } -int createchannel(struct channel *channels, char *name, char *topic, char *topicwho, char *topicwhen) { +int createchannel(struct channel *channels, struct ircdstate *ircdstate, char *name, char *topic, char *topicwho, char *topicwhen) { debugprint(DEBUG_FULL, "createchannel(): given '%s', '%s', '%s', and '%s'.\n", name, topic, topicwho, topicwhen); // Make sure the channel doesn't already exist - for (int i = 0; i < MAXCHANNELS; i++) { - if (strncmp(channels[i].name, name, strlen(name)) == 0) { + for (int i = 0; i < ircdstate->maxchannelcount; i++) { + if (strlen(channels[i].name) == strlen(name) && strncmp(channels[i].name, name, strlen(name)) == 0) { // Note that this may be happening because we just reconnected debugprint(DEBUG_CRIT, "error: createchannel(): channel name already exists.\n"); return 0; @@ -645,44 +645,66 @@ int createchannel(struct channel *channels, char *name, char *topic, char *topic } } - // Find a free slot in the array (when the channel name is not set (0th character is '\0'))... - for (int i = 0; i < MAXCHANNELS; i++) { + // Make sure we aren't in too many channels already + if (getchannelcount(channels, ircdstate->maxchannelcount) >= MAXCHANNELS - 1) { + debugprint(DEBUG_CRIT, "error: createchannel(): already in too many channels (MAXCHANNELS = %d!\n", MAXCHANNELS); + return 0; + } + + int arrslot = -1; + + // See if there's a free slot in the already used section of the channels array + for (int i = 0; i < ircdstate->maxchannelcount; i++) { if (!channels[i].name[0]) { - // ...and set the name and topic - strncpy(channels[i].name, name, strlen(name)); - channels[i].name[strlen(name)] = '\0'; - debugprint(DEBUG_FULL, "createchannel(): name given was '%s', length '%ld'.\n", name, strlen(name)); - debugprint(DEBUG_FULL, "createchannel(): name set to '%s', length '%ld'.\n", channels[i].name, strlen(channels[i].name)); - strncpy(channels[i].topic, topic, strlen(topic)); - channels[i].topic[strlen(topic)] = '\0'; - debugprint(DEBUG_FULL, "createchannel(): topic given was '%s', length '%ld'.\n", topic, strlen(topic)); - debugprint(DEBUG_FULL, "createchannel(): topic set to '%s', length '%ld'.\n", channels[i].topic, strlen(channels[i].topic)); - strncpy(channels[i].topicwho, topicwho, strlen(topicwho)); - channels[i].topicwho[strlen(topicwho)] = '\0'; - 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; + // We found one, use this slot + arrslot = i; + debugprint(DEBUG_FULL, "createchannel(): re-using existing slot %d.\n", arrslot); + break; } } + // If we didn't find one, increment the total count and use the next unused slot + if (arrslot < 0) { + arrslot = ircdstate->maxchannelcount; + ircdstate->maxchannelcount++; + debugprint(DEBUG_FULL, "createchannel(): using new slot slot %d.\n", arrslot); + } + + // If we got a valid slot... + if (arrslot >= 0) { + // ...set the name and topic + strncpy(channels[arrslot].name, name, strlen(name)); + channels[arrslot].name[strlen(name)] = '\0'; + debugprint(DEBUG_FULL, "createchannel(): name given was '%s', length '%ld'.\n", name, strlen(name)); + debugprint(DEBUG_FULL, "createchannel(): name set to '%s', length '%ld'.\n", channels[arrslot].name, strlen(channels[arrslot].name)); + strncpy(channels[arrslot].topic, topic, strlen(topic)); + channels[arrslot].topic[strlen(topic)] = '\0'; + debugprint(DEBUG_FULL, "createchannel(): topic given was '%s', length '%ld'.\n", topic, strlen(topic)); + debugprint(DEBUG_FULL, "createchannel(): topic set to '%s', length '%ld'.\n", channels[arrslot].topic, strlen(channels[arrslot].topic)); + strncpy(channels[arrslot].topicwho, topicwho, strlen(topicwho)); + channels[arrslot].topicwho[strlen(topicwho)] = '\0'; + strncpy(channels[arrslot].topicwhen, topicwhen, strlen(topicwhen)); + channels[arrslot].topicwhen[strlen(topicwhen)] = '\0'; + channels[arrslot].gotnames = 0; + // Set nicks to blank + for (int i = 0; i < MAXCHANNICKS; i++) { + channels[arrslot].nicks[i][0] = '\0'; + } + return 1; + } + debugprint(DEBUG_CRIT, "error: createchannel() didn't create a channel\n"); // TODO - Make a failed return do something to callers return 0; } -int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when) { +int setchanneltopicwhotime(struct channel *channels, int maxchannelcount, char *channelname, char *who, char *when) { debugprint(DEBUG_FULL, "setchanneltopicwhotime(): given '%s', '%s', and '%s'.\n", channelname, who, when); - debugprint(DEBUG_FULL, "setchanneltopicwhotime(): who: '%s' with length '%ld'.\n", who, strlen(who)); debugprint(DEBUG_FULL, "setchanneltopicwhotime(): when: '%s' with length '%ld'.\n", when, strlen(when)); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { strncpy(channels[i].topicwho, who, strlen(who)); channels[i].topicwho[strlen(who)] = '\0'; @@ -696,10 +718,10 @@ int setchanneltopicwhotime(struct channel *channels, char *channelname, char *wh return 0; } -int setchanneltopic(struct channel *channels, char *channelname, char *topic) { +int setchanneltopic(struct channel *channels, int maxchannelcount, char *channelname, char *topic) { debugprint(DEBUG_FULL, "setchanneltopic(): given '%s' and '%s'.\n", channelname, topic); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { strncpy(channels[i].topic, topic, strlen(topic)); channels[i].topic[strlen(topic)] = '\0'; @@ -711,10 +733,10 @@ int setchanneltopic(struct channel *channels, char *channelname, char *topic) { return 0; } -int getchannelcount(struct channel *channels) { +int getchannelcount(struct channel *channels, int maxchannelcount) { int count = 0; - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (channels[i].name[0]) { count++; } @@ -725,14 +747,14 @@ int getchannelcount(struct channel *channels) { return count; } -int removechannel(struct channel *channels, char *name) { +int removechannel(struct channel *channels, int maxchannelcount, char *name) { debugprint(DEBUG_FULL, "removechannel(): given '%s'.\n", name); // Clear its topic setter and timestamp... - setchanneltopicwhotime(channels, name, "", "0"); + setchanneltopicwhotime(channels, maxchannelcount, name, "", "0"); // Find the channel in the channel array... - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { // ..and NULL its name (0th character = '\0') channels[i].name[0] = '\0'; @@ -753,9 +775,9 @@ int removechannel(struct channel *channels, char *name) { // Check if we have the NAMES for the channel 'name' already. // Return the 1 if we do, 0 if we don't, or -1 if there's an error. -int channelgotnames(struct channel *channels, char *name) { +int channelgotnames(struct channel *channels, int maxchannelcount, char *name) { debugprint(DEBUG_FULL, "channelgotnames(): given '%s'.\n", name); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { if (channels[i].gotnames) { debugprint(DEBUG_FULL, "channelgotnames(): channel '%s' gotnames was set, returning '%d'.\n", channels[i].name, channels[i].gotnames); @@ -774,7 +796,7 @@ int channelgotnames(struct channel *channels, char *name) { // Check if we are in a channel named "name" or not. // Return 1 if we are, or 0 if not. -int inchannel(struct channel *channels, char *name) { +int inchannel(struct channel *channels, int maxchannelcount, char *name) { // Make sure the name doesn't have any trailing CR or LF // (But only if name is at least two characters long already) if (strlen(name) >= 2) { @@ -783,7 +805,7 @@ int inchannel(struct channel *channels, char *name) { } } - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { debugprint(DEBUG_FULL, "inchannel(): in channel '%s'.\n", name); return 1; @@ -798,9 +820,9 @@ int inchannel(struct channel *channels, char *name) { // Returns the array index in the 'channels' array of the channel // named 'channel'. // Returns -1 if there was an error. -int channelindex(struct channel *channels, char *name) { +int channelindex(struct channel *channels, int maxchannelcount, char *name) { debugprint(DEBUG_FULL, "channelindex(): given '%s'.\n", name); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { return i; } @@ -883,7 +905,7 @@ int doreplay(int sourcefd, int replayseconds, struct client *clients, struct set extractnickfromprefix(tokens[0], 1); // Check if we're currently in this channel or if the log line is from us - if (!inchannel(channels, tokens[2] + offset) || strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0) { + if (!inchannel(channels, ircdstate->maxchannelcount, tokens[2] + offset) || strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0) { debugprint(DEBUG_FULL, "Not sending '%s' replay line '%s'.\n", tokens[1], outgoingmsg); free(strcopyPtr); continue; @@ -1348,7 +1370,7 @@ 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) { +int addnicktochannel(char *nickuserhost, char *channel, struct channel *channels, int maxchannelcount) { debugprint(DEBUG_FULL, "addnicktochannel(): given '%s' and '%s'.\n", nickuserhost, channel); // Get the nick from the prefix @@ -1357,7 +1379,7 @@ int addnicktochannel(char *nickuserhost, char *channel, struct channel *channels // Make sure the channel exists int chanfound = 0; int chanindex; - for (chanindex = 0; chanindex < MAXCHANNELS; chanindex++) { + for (chanindex = 0; chanindex < maxchannelcount; chanindex++) { if (strlen(channels[chanindex].name) == strlen(channel) && !strcmp(channels[chanindex].name, channel)) { chanfound = 1; break; @@ -1393,7 +1415,7 @@ int addnicktochannel(char *nickuserhost, char *channel, struct channel *channels // 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) { +int removenickfromchannel(char *nickuserhost, char *channel, struct channel *channels, int maxchannelcount) { debugprint(DEBUG_FULL, "removenickfromchannel(): given '%s' and '%s'.\n", nickuserhost, channel); // Get the username from the prefix @@ -1402,7 +1424,7 @@ int removenickfromchannel(char *nickuserhost, char *channel, struct channel *cha // Make sure the channel exists int chanfound = 0; int chanindex; - for (chanindex = 0; chanindex < MAXCHANNELS; chanindex++) { + for (chanindex = 0; chanindex < maxchannelcount; chanindex++) { if (strlen(channels[chanindex].name) == strlen(channel) && !strcmp(channels[chanindex].name, channel)) { chanfound = 1; break; @@ -1432,7 +1454,7 @@ int removenickfromchannel(char *nickuserhost, char *channel, struct channel *cha // 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) { +int removenickfromallchannels(char *nickuserhost, struct channel *channels, int maxchannelcount) { debugprint(DEBUG_FULL, "removenickfromallchannels(): given '%s'.\n", nickuserhost); // Get the nick from the prefix @@ -1440,12 +1462,12 @@ int removenickfromallchannels(char *nickuserhost, struct channel *channels) { // Make sure the nick has a length of at least one if (strlen(nickuserhost) < 1) { - debugprint(DEBUG_CRIT, "updatenickinallchannels(): nick has no length, returning 0!\n"); + debugprint(DEBUG_CRIT, "removenickfromallchannels(): nick has no length, returning 0!\n"); return 0; } // Go through all channels and remove nick if present - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { // Don't bother checking this channel index if it isn't used if (!channels[i].name[0]) { continue; @@ -1466,7 +1488,7 @@ int removenickfromallchannels(char *nickuserhost, struct channel *channels) { // 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) { +int updatenickinallchannels(char *nickuserhost, char *newnick, struct channel *channels, int maxchannelcount) { debugprint(DEBUG_FULL, "updatenickinallchannels(): given '%s' and '%s'.\n", nickuserhost, newnick); // Get the nick from the prefix @@ -1482,7 +1504,7 @@ int updatenickinallchannels(char *nickuserhost, char *newnick, struct channel *c } // Go through all channels and update nick if present - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < maxchannelcount; i++) { // Go through all nicks in channel for (int j = 0; j < MAXCHANNICKS; j++) { // Update the nick in the channel if present @@ -1498,7 +1520,7 @@ int updatenickinallchannels(char *nickuserhost, char *newnick, struct channel *c // 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) { +int addnamereplytochannel(char *namereply, struct channel *channels, int maxchannelcount) { //:irc.tghost.co.uk 353 blabounce = #blabouncer :blabounce bbnick ~@l_bratch @l_blabnc Hughbla Bratchbot ars debugprint(DEBUG_FULL, "addnamereplytochannel(): given '%s'.\n", namereply); @@ -1572,7 +1594,7 @@ int addnamereplytochannel(char *namereply, struct channel *channels) { for (int i = 0; i < nickcount; i++) { stripprefixesfromnick(nicks[i]); // And add to the channel - addnicktochannel(nicks[i], channelname, channels); + addnicktochannel(nicks[i], channelname, channels, maxchannelcount); } return 1; diff --git a/functions.h b/functions.h index 7a3e398..49417e8 100644 --- a/functions.h +++ b/functions.h @@ -118,28 +118,28 @@ int sendtoserver(SSL *server_ssl, char *strsrc, int str_len, int clientfd, struc // Also set the pending statuses to 0 int disconnectclient(int fd, struct client *clients, struct ircdstate *ircdstate, struct settings *settings, struct clientcodes *clientcodes); -int createchannel(struct channel *channels, char *name, char *topic, char *topicwho, char *topicwhen); +int createchannel(struct channel *channels, struct ircdstate *ircdstate, char *name, char *topic, char *topicwho, char *topicwhen); -int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when); +int setchanneltopicwhotime(struct channel *channels, int maxchannelcount, char *channelname, char *who, char *when); -int setchanneltopic(struct channel *channels, char *channelname, char *topic); +int setchanneltopic(struct channel *channels, int maxchannelcount, char *channelname, char *topic); -int getchannelcount(struct channel *channels); +int getchannelcount(struct channel *channels, int maxchannelcount); -int removechannel(struct channel *channels, char *name); +int removechannel(struct channel *channels, int maxchannelcount, char *name); // Check if we have the NAMES for the channel 'name' already. // Return the 1 if we do, 0 if we don't, or -1 if there's an error. -int channelgotnames(struct channel *channels, char *name); +int channelgotnames(struct channel *channels, int maxchannelcount, char *name); // Check if we are in a channel named "name" or not. // Return 1 if we are, or 0 if not. -int inchannel(struct channel *channels, char *name); +int inchannel(struct channel *channels, int maxchannelcount, char *name); // Returns the array index in the 'channels' array of the channel // named 'channel'. // Returns -1 if there was an error. -int channelindex(struct channel *channels, char *name); +int channelindex(struct channel *channels, int maxchannelcount, char *name); // Send the requested number of lines of replay log to the requested client. // 'sourcefd' is the client to send to, and replayseconds is the number of @@ -196,23 +196,23 @@ 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); +int addnicktochannel(char *nickuserhost, char *channel, struct channel *channels, int maxchannelcount); // 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); +int removenickfromchannel(char *nickuserhost, char *channel, struct channel *channels, int maxchannelcount); // 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); +int removenickfromallchannels(char *nickuserhost, struct channel *channels, int maxchannelcount); // 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); +int updatenickinallchannels(char *nickuserhost, char *newnick, struct channel *channels, int maxchannelcount); // 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); +int addnamereplytochannel(char *namereply, struct channel *channels, int maxchannelcount); // Strips all leading prefixes (colons, user modes) from a nick void stripprefixesfromnick(char *nick); diff --git a/message.c b/message.c index 8431c31..e30a247 100644 --- a/message.c +++ b/message.c @@ -116,14 +116,11 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } // Next re-join channels - // Storing separately so we can skip over blank channels. - int channelcount = getchannelcount(channels); - // Join all the channels and make a note of the channel count - for (int i = 0; i < channelcount; i++) { - // Skip this one and increment channelcount if it's a blank channel + // Join all the channels + for (int i = 0; i < ircdstate->maxchannelcount; i++) { + // Skip this one if it's a blank channel if (!channels[i].name[0]) { - debugprint(DEBUG_FULL, "Reconnection: Skipping channel[%d], incrementing channelcount.\n", i); - channelcount++; + debugprint(DEBUG_FULL, "Reconnection: Skipping blank channel channel[%d].\n", i); continue; } @@ -197,13 +194,13 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int debugprint(DEBUG_FULL, "Server JOIN: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstate->ircnick); // TODO - Saner way to initialise this since we don't have the variables yet? // TODO - Defaulting to type '=' which is "public" since I don't know what else to guess. - createchannel(channels, tokens[2], "TOPIC", "TOPICWHO", "0"); + createchannel(channels, ircdstate, tokens[2], "TOPIC", "TOPICWHO", "0"); } else { debugprint(DEBUG_FULL, "Server JOIN: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstate->ircnick); } // Add the JOINing nick to our local channel struct - if (!addnicktochannel(tokens[0], tokens[2], channels)) { + if (!addnicktochannel(tokens[0], tokens[2], channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to add nick to channel struct.\n"); } @@ -236,13 +233,13 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int extractnickfromprefix(prefixcopy, 1); if (strncmp(prefixcopy, ircdstate->ircnick, strlen(tokens[0])) == 0) { debugprint(DEBUG_FULL, "Server PART: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstate->ircnick); - removechannel(channels, tokens[2]); + removechannel(channels, ircdstate->maxchannelcount, tokens[2]); } else { debugprint(DEBUG_FULL, "Server PART: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstate->ircnick); } // Remove the PARTing nick from our local channel struct - if (!removenickfromchannel(tokens[0], tokens[2], channels)) { + if (!removenickfromchannel(tokens[0], tokens[2], channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to remove nick from channel struct.\n"); } @@ -280,7 +277,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int char quitnick[MAXDATASIZE]; strcpy(quitnick, tokens[0]); extractnickfromprefix(quitnick, 1); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < ircdstate->maxchannelcount; i++) { if (channels[i].name[0]) { for (int j = 0; j < MAXCHANNICKS; j++) { if (strlen(channels[i].nicks[j]) == strlen(quitnick) && !strcmp(channels[i].nicks[j], quitnick)) { @@ -295,7 +292,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } // Remove the QUITting nick from our local channel struct - if (!removenickfromallchannels(tokens[0], channels)) { + if (!removenickfromallchannels(tokens[0], channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to remove nick from channel structs.\n"); } @@ -306,9 +303,9 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int // Server 331 (RPL_NOTOPIC) the topic is blank which we track by having a set timestamp of 0 if (strncmp(tokens[1], "331", strlen(tokens[1])) == 0) { // Might as well blank our current topic value - setchanneltopic(channels, tokens[3], ""); + setchanneltopic(channels, ircdstate->maxchannelcount, tokens[3], ""); // Set the topic timestamp to 0 which we use to determine an "unset" topic when new clients connect - setchanneltopicwhotime(channels, tokens[3], "", "0"); + setchanneltopicwhotime(channels, ircdstate->maxchannelcount, tokens[3], "", "0"); // Server 332 (RPL_TOPIC) set the channel topic } else if (strncmp(tokens[1], "332", strlen(tokens[1])) == 0) { debugprint(DEBUG_FULL, "Server 332 (RPL_TOPIC) found, extracting topic and storing in channel struct.\n"); @@ -316,17 +313,17 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int // Copy to a temporary string so we still have the original in case we need it char *topiccopy = strdup(str); extractfinalparameter(topiccopy); - setchanneltopic(channels, tokens[3], topiccopy); + setchanneltopic(channels, ircdstate->maxchannelcount, tokens[3], topiccopy); free(topiccopy); // Server 333 (RPL_TOPICWHOTIME) set the channel topic setter and the time it was set } else if (strncmp(tokens[1], "333", strlen(tokens[1])) == 0) { debugprint(DEBUG_FULL, "Server 333 (RPL_TOPICWHOTIME) found, extracting who and when, and storing in channel struct.\n"); - setchanneltopicwhotime(channels, tokens[3], tokens[4], tokens[5]); + setchanneltopicwhotime(channels, ircdstate->maxchannelcount, tokens[3], tokens[4], tokens[5]); // Server 353 (RPL_NAMREPLY), relay to all clients if we've just JOINed the channel, or relay to any clients // who were pending RPL_NAMREPLYs if it's an existing channel. } else if (strncmp(tokens[1], "353", strlen(tokens[1])) == 0) { // It must be a new channel and we don't have the NAMES - if (!channelgotnames(channels, tokens[4])) { + if (!channelgotnames(channels, ircdstate->maxchannelcount, tokens[4])) { debugprint(DEBUG_FULL, "Server 353 received for a new channel, sending to all clients.\n"); // Relay to all clients sendtoallclients(clients, str, sourcefd, settings); @@ -344,7 +341,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } // Update our local channels struct with all nicks from this RPL_NAMREPLY - if (!addnamereplytochannel(str, channels)) { + if (!addnamereplytochannel(str, channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to add RPL_NAMREPLY to channels.\n"); } @@ -354,10 +351,10 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } else if (strncmp(tokens[1], "366", strlen(tokens[1])) == 0) { int channelelement; // It must be a new channel and we don't have the NAMES - if (!(channelelement = channelgotnames(channels, tokens[3]))) { + if (!(channelelement = channelgotnames(channels, ircdstate->maxchannelcount, tokens[3]))) { debugprint(DEBUG_FULL, "Server 366 received for a new channel, sending to all clients and set as got names.\n"); // We have the names now! - channels[channelindex(channels, tokens[3])].gotnames = 1; + channels[channelindex(channels, ircdstate->maxchannelcount, tokens[3])].gotnames = 1; // Relay to all clients sendtoallclients(clients, str, sourcefd, settings); } else { @@ -389,7 +386,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int // Copy to a temporary string so we still have the original in case we need it char *topiccopy = strdup(str); extractfinalparameter(topiccopy); - setchanneltopic(channels, tokens[2], topiccopy); + setchanneltopic(channels, ircdstate->maxchannelcount, tokens[2], topiccopy); // Extract the author and get the current timestamp @@ -405,7 +402,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int snprintf(timenowstr, timenowlen + 1, "%ld", timenow); // Actually set the author and timestamp - setchanneltopicwhotime(channels, tokens[2], prefixcopy, timenowstr); + setchanneltopicwhotime(channels, ircdstate->maxchannelcount, tokens[2], prefixcopy, timenowstr); // And then finally relay to all clients sendtoallclients(clients, str, sourcefd, settings); @@ -493,7 +490,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int char oldnick[MAXDATASIZE]; strcpy(oldnick, tokens[0]); extractnickfromprefix(oldnick, 1); - for (int i = 0; i < MAXCHANNELS; i++) { + for (int i = 0; i < ircdstate->maxchannelcount; i++) { if (channels[i].name[0]) { for (int j = 0; j < MAXCHANNICKS; j++) { if (strlen(channels[i].nicks[j]) == strlen(oldnick) && !strcmp(channels[i].nicks[j], oldnick)) { @@ -508,7 +505,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int } // Update old nick to the new nick in our local channel struct - if (!updatenickinallchannels(tokens[0], tokens[2], channels)) { + if (!updatenickinallchannels(tokens[0], tokens[2], channels, ircdstate->maxchannelcount)) { debugprint(DEBUG_CRIT, "Failed to update old nick to new nick in channels.\n"); } @@ -970,19 +967,18 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstate->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); - // Get the channel count so we can enumerate over all channels. - // Storing separately so we can skip over blank channels. - int channelcount = getchannelcount(channels); + // Get the channel count so we can iterate over all channels. + int channelcount = getchannelcount(channels, ircdstate->maxchannelcount); // Set the client as pending RPL_NAMREPLYs for 'channelcount' channels debugprint(DEBUG_FULL, "Setting pendingnames to '%d' for client with fd '%d'.\n", channelcount, sourcefd); clients[arrindex(clients, sourcefd)].pendingnames = channelcount; // Get client to join channels, and tell client about those channels - for (int i = 0; i < channelcount; i++) { + for (int i = 0; i < ircdstate->maxchannelcount; i++) { debugprint(DEBUG_FULL, "JOINing channel[%d] out of %d.\n", i, channelcount); // Skip this one and increment channelcount if it's a blank channel if (!channels[i].name[0]) { - debugprint(DEBUG_FULL, "Skipping channel[%d], incrementing channelcount.\n", i); + debugprint(DEBUG_FULL, "Actually, skipping blank channel channel[%d].\n", i); channelcount++; continue; } diff --git a/structures.h b/structures.h index 0fbebff..26b7439 100644 --- a/structures.h +++ b/structures.h @@ -55,6 +55,7 @@ struct ircdstate { int clientchangetime; // The last time a client registered or disconnected int clientsnonetime; // The last time there were no clients registered int launchtime; // The time blabouncer was launched + int maxchannelcount; // The maximum number of channels we've ever been in (so we know how much of the channels struct is initialised) }; // Structure of settings either to be read from the configuration file or set/changed at runtime -- cgit v1.2.3