summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README3
-rw-r--r--TODO4
-rw-r--r--blabouncer.c18
-rw-r--r--config.c181
-rw-r--r--config.h6
-rw-r--r--message.c8
-rw-r--r--structures.h3
7 files changed, 209 insertions, 14 deletions
diff --git a/README b/README
index ce415fa..e2a026f 100644
--- a/README
+++ b/README
@@ -20,6 +20,9 @@ All arguments are optional, but they must be specified in the order shown above
An example configuration file is provided named "blabouncer.conf.example".
+Configuration options are either simple single string options, or multiple line arrays. The usage is
+explained in the example configuration file.
+
If you don't specify one using "-c /path/to/configuration/file" then the example one will be created
for you at $HOME/.blabouncer/blabouncer.conf when starting for the first time.
diff --git a/TODO b/TODO
index f0071c3..7b94d3d 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,4 @@
-Support multiple connect commands.
-
-Support arrays or similar in the configuration file (for nick(s), connectcommand(s), etc.)
+Specify multiple nicks using configuration arrays.
All the TODOs sprinkled throughout the code!
diff --git a/blabouncer.c b/blabouncer.c
index d94a165..e6a56c1 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -1025,9 +1025,21 @@ int main(int argc, char *argv[]) {
settings.ircserverpassword[0] = '\0';
}
- // What is the connect command, if any?
- if (!getconfstr("connectcommand", settings.conffile, settings.connectcommand)) {
- settings.connectcommand[0] = '\0';
+ // What are the connect commands, if any?
+ int ret = getconfarr("connectcommands", settings.conffile, settings.connectcommands);
+ if (!ret) {
+ for (int i = 0; i < MAXCONFARR; i++) {
+ settings.connectcommands[i][0] = '\0';
+ }
+ } else if (ret == -1) {
+ // Remove any newlines from the middle of the string so error printing works nicely
+ for (size_t i = 0; i < strlen(settings.connectcommands[0]) - 1; i++) {
+ if (settings.connectcommands[0][i] == '\n') {
+ settings.connectcommands[0][i] = ' ';
+ }
+ }
+ printf("main(): error getting 'commandcommands' from configuration file: %s", settings.connectcommands[0]);
+ exit(1);
}
// Is the base directory set? If not, use the default.
diff --git a/config.c b/config.c
index cea404c..842f389 100644
--- a/config.c
+++ b/config.c
@@ -25,7 +25,7 @@ int getconfstr(char *confname, char *filename, char* dest) {
char str[MAXCHAR];
int found = 0; // Have we found the configuration option?
- // Set strings to zero-length to begin
+ // Set string to zero-length to begin
dest[0] = '\0';
// Length of requested configuration option name
@@ -105,6 +105,165 @@ int getconfstr(char *confname, char *filename, char* dest) {
return 1;
}
+// Populates 'dest' with the values of the configuration array option
+// with name 'confname' from configuration file 'filename'.
+// Returns 1 on success, 0 on reading no values, or -1 on an error.
+// On error, dest[0] is set to the error string for later retrieval.
+int getconfarr(char *confname, char *filename, char dest[MAXCONFARR][MAXDATASIZE]) {
+ debugprint(DEBUG_FULL, "getconfarr(): '%s', '%s' and a dest array.\n", confname, filename);
+
+ FILE *fp;
+ char line[MAXCHAR];
+ int found = 0; // Have we found the configuration option?
+ int valuecount = 0; // Which element in the configuration array we are on
+
+ // Set strings to zero-length to begin
+ for (int i = 0; i < MAXCONFARR; i++) {
+ dest[i][0] = '\0';
+ }
+
+ // Length of requested configuration array name
+ long int namelen = strlen(confname);
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ debugprint(DEBUG_CRIT, "error: could not open configuration file '%s'.\n", filename);
+ exit(1);
+ }
+ // Loop through the whole file, looking for the requested configuration array
+ while (fgets(line, MAXCHAR, fp) != NULL) {
+ // Don't bother with any of this if this line is a comment
+ int comment = 0;
+ for (size_t i = 0; i < strlen(line); i++) {
+ // Ignore spaces/tabs
+ if (line[i] == ' ' || line[i] == '\t') {
+ continue;
+ } else if (line[i] == '#') {
+ // If it's a comment, ignore the line
+ comment = 1;
+ break;
+ } else {
+ // Found something else before a comment, so carry on
+ break;
+ }
+ }
+
+ if (comment) {
+ continue;
+ }
+
+ // If we haven't found our array yet, try to find the opening line
+ if (!found) {
+ char substr[MAXCHAR];
+
+ // Check if the next character after the length of the requested array
+ // name is an equals sign, a space, or a tab
+ if (line[namelen] != '=' && line[namelen] != ' ' && line[namelen] != '\t') {
+ // If it isn't this can't have been our array
+ continue;
+ }
+
+ // Copy the number of characters that the requested array name is long
+ // to a temporary string
+ strncpy(substr, line, namelen);
+ substr[namelen] = '\0';
+
+ // If the resulting temporary string contains the requested array name,
+ // we have found our configuration array
+ if (strstr(substr, confname)) {
+ // Make sure it is a valid start of array line
+ for (size_t i = namelen; i < strlen(line); i++) {
+ if (line[i] == ' ' || line[i] == '\t' || line[i] == '=') {
+ // Ignore spaces, tabs and equals signs
+ continue;
+ } else if (line[i] == '{') {
+ // Success, found an opening brace
+ // Ignore anything else on this line
+ found = 1;
+ break;
+ } else {
+ // Unexpected character found, return failure
+ snprintf(dest[0], MAXDATASIZE, "Unexpected character '%c' found on configuration array opening line for '%s'.\n", line[i], confname);
+ fclose(fp);
+ return -1;
+ }
+ }
+ }
+ // If we have found our array, extract the value from each line in it
+ } else {
+ int valuelen = 0;
+ int inquotes = 0;
+ for (size_t i = 0; i < strlen(line); i++) {
+ // If we've on the closing brace line, then we're done
+ if (line[i] == '}') {
+ for (int i = 0; i < valuecount; i++) {
+ debugprint(DEBUG_FULL, "getconfstr(): returning '%s'.\n", dest[i]);
+ }
+
+ // Close fine and return success (or 0 if no values found in an otherwise valid array)
+ fclose(fp);
+ if (valuecount) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // If not in the quotes yet
+ if (!inquotes) {
+ // Skip over initial spaces and tabs
+ if (line[i] == ' ' || line[i] == '\t') {
+ continue;
+ } else if (line[i] == '"') {
+ // Quotes found, we're now reading the value
+ inquotes = 1;
+ continue;
+ } else {
+ // Unexpected character found, return failure
+ snprintf(dest[0], MAXDATASIZE, "Unexpected character '%c' found before opening quotes on array line for '%s'.\n", line[i], confname);
+ fclose(fp);
+ return -1;
+ }
+ // If inside the quotes (so, we've got to the actual value)
+ } else if (inquotes) {
+ // If we're on the last character and it isn't a closing quote, something is wrong
+ if (i == strlen(line) - 1 && line[i] != '"') {
+ snprintf(dest[0], MAXDATASIZE, "Reached end of line without finding closing quotes on array line for '%s'.\n", confname);
+ fclose(fp);
+ return -1;
+ }
+
+ // If we've found too many values, return an error
+ if (valuecount > MAXCONFARR) {
+ snprintf(dest[0], MAXDATASIZE, "Too many elements defined for configuration array '%s', maximum number is '%d'.\n", confname, MAXCONFARR);
+ fclose(fp);
+ return -1;
+ }
+
+ // Otherwise, copy everything that isn't the closing quotes to the current element in the dest array
+ if (line[i] != '"') {
+ dest[valuecount][valuelen] = line[i];
+ valuelen++;
+ continue;
+ } else {
+ // When we find the closing quotes, the value is read, so terminate the dest array element string
+ dest[valuecount][valuelen] = '\0';
+ // We're done with this value, ignore anything that may be after the closing quotes on this line
+ valuecount++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // If we get this far, then something went wrong
+ snprintf(dest[0], MAXDATASIZE, "getconfarr(): didn't find any configuration array for '%s'.\n", confname);
+ fclose(fp);
+ return 0;
+}
+
// Returns the value of the configuration option with name
// 'confname' from configuration file 'filename'.
// Sets errno to 0 on success, or ECONFINT if it fails, in which case the return value is undefined.
@@ -153,11 +312,22 @@ int createconfigfile(char *filename) {
char *string =
"# blabouncer configuration file\n"
"#\n"
- "# Entries must be in the form:\n"
+ "# Normal entries must be in the form:\n"
"# option name, space, equals sign, space, double quote, option value, double quote\n"
"# e.g.\n"
"# realname = \"Mr Bla Bouncer\"\n"
"#\n"
+ "# Array entries must be in the form:\n"
+ "# option name, space, equals sign, space, open brace\n"
+ "# (optional indentation,) double quote, element value, double quoute\n"
+ "# (optional multiple values to be repeated after the first one(s))\n"
+ "# close brace\n"
+ "# e.g.\n"
+ "# connectcommands = {\n"
+ "# \"PRIVMSG NickServ IDENTIFY bananas\"\n"
+ "# \"PRIVMSG myfriend I'm online!\"\n"
+ "# }\n"
+ "#\n"
"# Shell expansion is not supported, so do not try and specify e.g.\n"
"# \"~/.blabouncer/\" or \"$HOME/.blabouncer/\", instead use \"/home/foo/.blabouncer\"\n"
"#\n"
@@ -210,8 +380,11 @@ int createconfigfile(char *filename) {
"# Real IRC server password\n"
"#ircserverpassword = \"apples\"\n"
"\n"
- "# Command to send to the server upon completing registration (e.g. a NickServ password)\n"
- "#connectcommand \"PRIVMSG NickServ IDENTIFY bananas\"\n"
+ "# Command(s) to send to the server upon completing registration (e.g. a NickServ password)\n"
+ "#connectcommands = {\n"
+ "# \"PRIVMSG NickServ IDENTIFY bananas\"\n"
+ "# \"PRIVMSG myfriend I'm online!\"\n"
+ "#}\n"
"\n"
"# Base directory (defaults to $HOME/.blabouncer/)\n"
"# Things such as the logs directory will be placed below this\n"
diff --git a/config.h b/config.h
index 2f2db9f..d478480 100644
--- a/config.h
+++ b/config.h
@@ -40,6 +40,12 @@
// getconfstr() returns.
int getconfstr(char *confname, char *filename, char* dest);
+// Populates 'dest' with the values of the configuration array option
+// with name 'confname' from configuration file 'filename'.
+// Returns 1 on success, 0 on reading no values, or -1 on an error.
+// On error, dest[0] is set to the error string for later retrieval.
+int getconfarr(char *confname, char *filename, char dest[MAXCONFARR][MAXDATASIZE]);
+
// Returns the value of the configuration option with name
// 'confname' from configuration file 'filename'.
// Sets errno to 0 on success, or ECONFINT if it fails, in which case the return value is undefined.
diff --git a/message.c b/message.c
index 5cfff91..3887ec9 100644
--- a/message.c
+++ b/message.c
@@ -89,9 +89,11 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
// Receiving greeting 004 means we're now registered
// Request IRCv3 multi-prefix extension so we can more accurately inform new clients about current user prefixes
sendtoserver(server_ssl, "CAP REQ multi-prefix", strlen("CAP REQ multi-prefix"), 0, clients, settings);
- // Send the connect command, if set
- if (settings->connectcommand[0]) {
- sendtoserver(server_ssl, settings->connectcommand, strlen(settings->connectcommand), 0, clients, settings);
+ // Send any configured connect commands
+ for (int i = 0; i < MAXCONFARR; i++) {
+ if (settings->connectcommands[i][0]) {
+ sendtoserver(server_ssl, settings->connectcommands[i], strlen(settings->connectcommands[i]), 0, clients, settings);
+ }
}
// If this is a reconnection, JOIN existing channels and catch clients up again
if (ircdstate->reconnecting) {
diff --git a/structures.h b/structures.h
index e58c1b1..8a99163 100644
--- a/structures.h
+++ b/structures.h
@@ -29,6 +29,7 @@
#define MAXAUTOCHANLEN 1024 // Randomly picked maximum length of the auto channel list
#define CLIENTCODELEN 17 // Max length of a client code + 1 for null
#define MAXCLIENTCODES 64 // Max number of client codes to track
+#define MAXCONFARR 10 // Max number of entries that a configuration array can have
struct ircdstate {
char greeting001[MAXDATASIZE];
@@ -70,7 +71,7 @@ struct settings {
char ircserver[HOST_NAME_MAX];
char ircserverport[MAXPORTLEN];
char ircserverpassword[MAXDATASIZE - 5]; // -5 for "PASS "
- char connectcommand[MAXDATASIZE];
+ char connectcommands[MAXCONFARR][MAXDATASIZE];
char conffile[PATH_MAX];
char certfile[PATH_MAX];
char keyfile[PATH_MAX];