From 4dea4c16313ba3d1575cfa6722d75492c907f551 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sat, 14 Sep 2019 20:44:32 +0100 Subject: Specify multiple nicks using a configuration array instead of multiple individual settings. --- README | 4 +-- TODO | 2 -- blabouncer.c | 39 ++++++++++++++++------------- blabouncer.conf.example | 31 ++++++++++++++++++----- config.c | 11 ++++++--- functions.c | 66 ++++++++++++++++++++++++++++++++----------------- message.c | 61 +++++++++++++++++++++------------------------ structures.h | 6 ++--- 8 files changed, 129 insertions(+), 91 deletions(-) diff --git a/README b/README index e2a026f..c688dae 100644 --- a/README +++ b/README @@ -30,9 +30,7 @@ Certain configuration options can be changed at runtime by changing them in the issuing a BLABOUNCER REHASH command, or by sending SIGHUP to the blabouncer process. These options can be changed by issuing a BLABOUNCER REHASH command or by sending SIGHUP to blabouncer: - - nick - - nick2 - - nick3 + - nicks - replaymode - replayseconds - replaydates diff --git a/TODO b/TODO index 7b94d3d..68e0122 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -Specify multiple nicks using configuration arrays. - All the TODOs sprinkled throughout the code! Is there a way to log nick changes to the normal log despite not tracking nicks in each channel? (We do track channel names themselves.) diff --git a/blabouncer.c b/blabouncer.c index e6a56c1..772b969 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -137,8 +137,8 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, // 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 - // Populate nick and username from our configuration file for now, real IRCd may change them later (TODO - Is this true of username?) - strcpy(ircdstate->ircnick, settings->ircnick); + // 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]); strcpy(ircdstate->ircusername, settings->ircusername); // Send the server password if one was configured @@ -967,22 +967,27 @@ int main(int argc, char *argv[]) { exit(1); } - // What is the configured nick? - if (!getconfstr("nick", settings.conffile, settings.ircnick)) { - printf("main(): error getting 'nick' from configuration file.\n"); + // What are the configured nick(s)? + int ret = getconfarr("nicks", settings.conffile, settings.ircnicks); + if (!ret) { + printf("main(): error getting any 'nicks' from configuration file.\n"); + } else if (ret == -1) { + // Error reading an array line from the configuration file + // Remove any newlines from the middle of the string so error printing works nicely + for (size_t i = 0; i < strlen(settings.ircnicks[0]) - 1; i++) { + if (settings.ircnicks[0][i] == '\n') { + settings.ircnicks[0][i] = ' '; + } + } + printf("main(): error getting 'nicks' from configuration file: %s", settings.ircnicks[0]); exit(1); } - - // What is the configured nick2? - if (!getconfstr("nick2", settings.conffile, settings.ircnick2)) { - // Not configured, set to blank string - settings.ircnick2[0] = '\0'; - } - - // What is the configured nick3? - if (!getconfstr("nick3", settings.conffile, settings.ircnick3)) { - // Not configured, set to blank string - settings.ircnick3[0] = '\0'; + // Make sure nicks aren't too long (since getconfarr() has to use MAXDATASIZE for all string lengths) + for (int i = 0; i < MAXCONFARR; i++) { + if (settings.ircnicks[i][0] && strlen(settings.ircnicks[i]) > MAXNICKLENGTH) { + printf("main(): error: specified nick '%s' is too long, maximum length is %d.\n", settings.ircnicks[i], MAXNICKLENGTH); + exit(1); + } } // What is the configured username? @@ -1026,7 +1031,7 @@ int main(int argc, char *argv[]) { } // What are the connect commands, if any? - int ret = getconfarr("connectcommands", settings.conffile, settings.connectcommands); + ret = getconfarr("connectcommands", settings.conffile, settings.connectcommands); if (!ret) { for (int i = 0; i < MAXCONFARR; i++) { settings.connectcommands[i][0] = '\0'; diff --git a/blabouncer.conf.example b/blabouncer.conf.example index 12727c9..0513fdc 100644 --- a/blabouncer.conf.example +++ b/blabouncer.conf.example @@ -1,18 +1,34 @@ # blabouncer configuration file # -# Entries must be in the form: +# Normal entries must be in the form: # option name, space, equals sign, space, double quote, option value, double quote # e.g. # realname = "Mr Bla Bouncer" # +# Array entries must be in the form: +# option name, space, equals sign, space, open brace +# (optional indentation,) double quote, element value, double quoute +# (optional multiple values to be repeated after the first one(s)) +# close brace +# e.g. +# connectcommands = { +# "PRIVMSG NickServ IDENTIFY bananas" +# "PRIVMSG myfriend I'm online!" +# } +# # Shell expansion is not supported, so do not try and specify e.g. # "~/.blabouncer/" or "$HOME/.blabouncer/", instead use "/home/foo/.blabouncer" # # Some settings can be reloaded at runtime, please refer to README for details. -nick = "blabounce" -nick2 = "bbounce2" -nick3 = "bbounce3" +# Nick(s) to use when connecting - will be cycled through in order in the event of +# a nick being in use or invalid +nicks = { + "blabounce" + "bbounce2" + "bbounce3" +} + username = "bounceusr" realname = "Mr Bla Bouncer" @@ -57,8 +73,11 @@ ircserverport = "6697" # Real IRC server password #ircserverpassword = "apples" -# Command to send to the server upon completing registration (e.g. a NickServ password) -#connectcommand "PRIVMSG NickServ IDENTIFY bananas" +# Command(s) to send to the server upon completing registration (e.g. a NickServ password) +#connectcommands = { +# "PRIVMSG NickServ IDENTIFY bananas" +# "PRIVMSG myfriend I'm online!" +#} # Base directory (defaults to $HOME/.blabouncer/) # Things such as the logs directory will be placed below this diff --git a/config.c b/config.c index 842f389..5b706b0 100644 --- a/config.c +++ b/config.c @@ -333,9 +333,14 @@ int createconfigfile(char *filename) { "#\n" "# Some settings can be reloaded at runtime, please refer to README for details.\n" "\n" - "nick = \"blabounce\"\n" - "nick2 = \"bbounce2\"\n" - "nick3 = \"bbounce3\"\n" + "# Nick(s) to use when connecting - will be cycled through in order in the event of\n" + "# a nick being in use or invalid\n" + "nicks = {\n" + " \"blabounce\"\n" + " \"bbounce2\"\n" + " \"bbounce3\"\n" + "}\n" + "\n" "username = \"bounceusr\"\n" "realname = \"Mr Bla Bouncer\"\n" "\n" diff --git a/functions.c b/functions.c index af8d5dd..fe96e0a 100644 --- a/functions.c +++ b/functions.c @@ -1086,31 +1086,51 @@ void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct irc int rehash(struct settings *settings, char *failuremsg) { // TODO - Try to share some/all of this code with the initial main() settings loading - // What is the nick? - char oldircnick[MAXNICKLENGTH]; - strcpy(oldircnick, settings->ircnick); - if (!getconfstr("nick", settings->conffile, settings->ircnick)) { - strcpy(settings->ircnick, oldircnick); - strcpy(failuremsg, "error getting 'nick' from configuration file"); + // What are the configured nick(s)? + char oldircnicks[MAXCONFARR][MAXDATASIZE]; + // Backup the existing configured nicks in case this rehash fails + for (int i = 0; i < MAXCONFARR; i++) { + strcpy(oldircnicks[i], settings->ircnicks[i]); + } + int ret = getconfarr("nicks", settings->conffile, settings->ircnicks); + if (!ret) { + // No nicks read, copy the old ones back + for (int i = 0; i < MAXCONFARR; i++) { + strcpy(settings->ircnicks[i], oldircnicks[i]); + } + strcpy(failuremsg, "error getting any 'nicks' from configuration file"); + return 0; + } else if (ret == -1) { + // Error reading an array line from the configuration file + // Remove any newlines from the string so error printing works nicely + for (size_t i = 0; i < strlen(settings->ircnicks[0]); i++) { + if (settings->ircnicks[0][i] == '\n') { + settings->ircnicks[0][i] = ' '; + } + } + if (!snprintf(failuremsg, MAXDATASIZE, "error getting 'nicks' from configuration file: %s", settings->ircnicks[0])) { + debugprint(DEBUG_CRIT, "Error while preparing nick error response!\n"); + strcpy(failuremsg, "Error while preparing nick error response!"); + } + // Copy the old ones back (after setting failuremsg so we can read the error string from element 0) + for (int i = 0; i < MAXCONFARR; i++) { + strcpy(settings->ircnicks[i], oldircnicks[i]); + } return 0; } - - // What is nick2? - char oldircnick2[MAXNICKLENGTH]; - strcpy(oldircnick2, settings->ircnick2); - if (!getconfstr("nick2", settings->conffile, settings->ircnick2)) { - strcpy(settings->ircnick2, oldircnick2); - // Not configured, set to blank string - settings->ircnick2[0] = '\0'; - } - - // What is nick3? - char oldircnick3[MAXNICKLENGTH]; - strcpy(oldircnick3, settings->ircnick3); - if (!getconfstr("nick3", settings->conffile, settings->ircnick3)) { - strcpy(settings->ircnick3, oldircnick3); - // Not configured, set to blank string - settings->ircnick3[0] = '\0'; + // Make sure nicks aren't too long (since getconfarr() has to use MAXDATASIZE for all string lengths) + for (int i = 0; i < MAXCONFARR; i++) { + if (settings->ircnicks[i][0] && strlen(settings->ircnicks[i]) > MAXNICKLENGTH) { + // A nick is too long, copy the old ones back + for (int i = 0; i < MAXCONFARR; i++) { + strcpy(settings->ircnicks[i], oldircnicks[i]); + } + if (!snprintf(failuremsg, MAXDATASIZE, "error: specified nick '%s' is too long, maximum length is %d.\n", settings->ircnicks[i], MAXNICKLENGTH)) { + debugprint(DEBUG_CRIT, "Error while preparing nick too long response!\n"); + strcpy(failuremsg, "Error while preparing nick too long response!"); + } + return 0; + } } // What is the auto replay mode? diff --git a/message.c b/message.c index 3887ec9..ed27173 100644 --- a/message.c +++ b/message.c @@ -64,6 +64,16 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int // Null the end of the new string ircdstate->nickuserhost[strlen(tokens[counter - 1]) + 1] = '\0'; // +1 for the inserted colon debugprint(DEBUG_FULL, "nickuserhost '%s' stored.\n", ircdstate->nickuserhost); + // Set our current ircnick based on whatever was in greeting 001 + if (counter >= 3) { + // Assuming there at least three tokens (:ircdname 001 nick etc.) then store the nick + strcpy(ircdstate->ircnick, tokens[2]); + debugprint(DEBUG_FULL, "Updated ircnick to '%s' from greeting 001.\n", ircdstate->ircnick); + } else { + // Something has gone fairly wrong with greeting 001 + debugprint(DEBUG_CRIT, "Greeting 001 ('%s') is not long enough, don't know how to proceed, exiting...\n", str); + exit(1); + } return 1; } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) { debugprint(DEBUG_FULL, "Found greeting 002 (%s), storing in ircdstate struct.\n", str); @@ -642,42 +652,27 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int debugprint(DEBUG_SOME, "Server 432 (ERR_ERRONEUSNICKNAME) or 433 (ERR_NICKNAMEINUSE) found and it is: %s with length %zd! Trying another nick...\n", tokens[1], strlen(tokens[1])); - // Do we have both a nick2 and a nick3? (And not tried autonick yet.) - if (settings->ircnick2[0] && settings->ircnick3[0] && !ircdstate->autonicknum) { - // Has nick3 already been tried? - if (strncmp(ircdstate->ircnick, settings->ircnick3, strlen(settings->ircnick)) == 0) { - // Then try autonick - tryautonick(ircdstate); - // Has nick2 already been tried? - } else if (strncmp(ircdstate->ircnick, settings->ircnick2, strlen(settings->ircnick)) == 0) { - // Then try nick3 - debugprint(DEBUG_SOME, "Trying nick3, nick2 was already tried.\n"); - strcpy(ircdstate->ircnick, settings->ircnick3); - // Have neither been tried? - } else { - // Then try nick2 - debugprint(DEBUG_SOME, "Trying nick2, nick3 is also configured.\n"); - strcpy(ircdstate->ircnick, settings->ircnick2); - } - // Do we only have a nick2? (And not tried autonick yet.) - } else if (settings->ircnick2[0] && !ircdstate->autonicknum) { - // Has it already been tried? - if (strncmp(ircdstate->ircnick, settings->ircnick2, strlen(settings->ircnick)) == 0) { - // Then try autonick - tryautonick(ircdstate); - } else { - // Then try it - debugprint(DEBUG_SOME, "Trying nick2, nick3 is not configured.\n"); - strcpy(ircdstate->ircnick, settings->ircnick2); + // Find the nick (its index in the nicks array) currently selected + int nickindex = -1; // -1 used later if current nick isn't in the configuration array + int nickcount = 0; // How many nicks are configured in the configuration array + for (int i = 0; i < MAXCONFARR; i++) { + if (settings->ircnicks[i][0]) { + nickcount++; + if (strncmp(ircdstate->ircnick, settings->ircnicks[i], strlen(settings->ircnicks[i])) == 0 && strlen(ircdstate->ircnick) == strlen(settings->ircnicks[i])) { + nickindex = i; + } } - // Do we have neither? (Or have already started autonick.) - } else { - // Then try autonick - tryautonick(ircdstate); } - // Set whichever one we settled on in the settings in case we reference settings later - strcpy(settings->ircnick, ircdstate->ircnick); + // If there are more nicks left to try, then try the next one + if (nickindex < nickcount - 1) { + strcpy(ircdstate->ircnick, settings->ircnicks[nickindex + 1]); + debugprint(DEBUG_SOME, "Switched nick to '%s' and retrying...\n", ircdstate->ircnick); + // Otherwise, give up on configured nicks and switch to autonick + } else { + debugprint(DEBUG_SOME, "Giving up on preconfigured nicks trying autonick...\n", ircdstate->ircnick); + tryautonick(ircdstate); + } // Try it with the server char outgoingmsg[MAXDATASIZE]; diff --git a/structures.h b/structures.h index 8a99163..8d68083 100644 --- a/structures.h +++ b/structures.h @@ -39,7 +39,7 @@ struct ircdstate { char greeting005a[MAXDATASIZE]; char greeting005b[MAXDATASIZE]; char greeting005c[MAXDATASIZE]; - char ircdname[MAXDATASIZE]; + char ircdname[MAXDATASIZE]; // In both settings and ircdstate as settings is from our file whereas server may change ircdstate copy char nickuserhost[MAXDATASIZE]; char ircnick[MAXNICKLENGTH]; char ircusername[MAXUSERNAMELEN]; @@ -61,9 +61,7 @@ struct settings { char replaymode[MAXDATASIZE]; int replayseconds; char clientport[MAXPORTLEN]; - char ircnick[MAXNICKLENGTH]; // In both settings and ircdstate as settings is from our file whereas server may change ircdstate copy - char ircnick2[MAXNICKLENGTH]; - char ircnick3[MAXNICKLENGTH]; + char ircnicks[MAXCONFARR][MAXDATASIZE]; // MAXDATASIZE instead of MAXNICKLENGTH so getconfarr() only has one string size to deal with char ircusername[MAXUSERNAMELEN]; // (Is this also true for the username? Can the server change that?) char ircrealname[MAXREALNAMELEN]; char password[MAXDATASIZE]; -- cgit v1.2.3