summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-06-10 20:35:05 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-06-10 20:35:05 +0100
commit5d21a0eb5bd7deed931078a302ca3b8d6881e62e (patch)
treef3990dcb60c579c59d83f136aa65e977d4853bb5
parent8e629c02b1a12e88110bcda7f0e67c6a74ff9741 (diff)
Refactoring - move most functions into functions.h/functions.c except connect/exit functions only relevant in main.c.
-rw-r--r--blabouncer.c563
-rw-r--r--functions.c557
-rw-r--r--functions.h77
3 files changed, 634 insertions, 563 deletions
diff --git a/blabouncer.c b/blabouncer.c
index e1f2a96..438107b 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -62,15 +62,12 @@
#define SIGINT 2 // SIGINT is signal 2
#define SIGTERM 15 // SIGTERM is signal 15
-// Various important limits - note that several related ones are defined in structures.h
+// Various important limits - note that several related ones are defined in functions.h and structures.h
// It seems to be that *message length* is max 512 bytes, but a socket read from at least UnrealIRCd seems to be up to at least 2416 (+1 for null) bytes.
// 1208 bytes with OpenSSL, 2416 bytes with plain text.
#define MAXRCVSIZE 2417
-#define MAXCLIENTS 32 // maximum number of clients that can connect to the bouncer at a time
#define MAXTOKENS 100 // maximum number of (CRLF or space) separated tokens per server response we expect (TODO - check this is reasonable) (CRLF and spaces just grouped here due to laziness)
-#define MAXCHANNELS 1024 // let's assume 1024 is reasonable for now (it's configured per IRCd)
-#define MAXRFCNICKLEN 9 // From RFC 1459
#define SERVERTIMEOUT 300 // How many seconds to wait without hearing from the server before assuming a timeout
// Global debug control
@@ -86,564 +83,6 @@ void sighandler(int sig) {
signum = sig;
}
-// Return index of requested client FD within the clients array.
-// TODO - Use this wherever we are calculating the position (various places) instead of
-// duplicating code.
-int arrindex(struct client *clients, int clientfd) {
- // Find the client in the clients array and make sure they are authenticated
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd == clientfd) {
- return i;
- }
- }
-
- // Something went wrong, we didn't find it
- return 0;
-}
-
-// Send whatever string to a specific client by providing the FD
-// If "bypass" == 1 then permit sending to client even if unauthenticated (for instance for a CAP LS response)
-int sendtoclient(int fd, char *strsrc, struct client *clients, struct settings *settings, int bypass) {
- // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
- char str[MAXDATASIZE];
- strcpy(str, strsrc);
-
- appendcrlf(str); // Do this just before sending so callers don't need to worry about it
-
- int i = 0;
- // Find the client in the clients array and make sure they are authenticated
- for (i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd == fd) {
- // Found client in array, check authentication status
- if (!clients[i].authed && !bypass) {
- debugprint(DEBUG_SOME, "sendtoclient(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
- return 0;
- }
- // Break when we get to the correct fd, "i" is now the position in the clients array of our client
- break;
- }
- }
-
- debugprint(DEBUG_SOME, "sendtoclient(): sending \"%s\" (length %zd) to client with fd %d.\n", str, strlen(str), fd);
- if (socksend(clients[i].ssl, str, strlen(str), settings->clienttls) == -1) {
- perror("error: sendtoclient() socksend()\n");
- debugprint(DEBUG_CRIT, "error: sendtoclient() socksend() error sending to client with fd '%d', errno '%d'.\n", fd, errno);
- return 0;
- }
-
- return 1;
-}
-
-// Relay/send message to all clients (optionally except one)
-// "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all)
-// "except" is really the "sourcefd" and is also used as part of the authentication check - this is messy and they should perhaps be two separate arguments.
-// TODO - is passing str_len useful if we're appendcrlfing and then using strlen(str) in the send? I guess not... (As long as we're always null terminated in the correct place.)
-int sendtoallclients(struct client *clients, char *strsrc, int except, struct settings *settings) {
-
- char *sendertype;
-
- // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
- char str[MAXDATASIZE];
- strcpy(str, strsrc);
-
- appendcrlf(str); // Do this just before sending so callers don't need to worry about it
-
- // Decide what sort of text to prefix the debug output with
- // At the moment if non-zero "except" is specified then it must be a message from a bouncer client
- // and if "except" is zero then it must be a message from the real IRC server
- if (except) {
- sendertype = "bouncer-client";
- } else {
- sendertype = "bouncer-server";
- }
-
- // Find the sending client in the clients array and make sure they are authenticated
- for (int i = 0; i < MAXCLIENTS; i++) {
- // Trust clientfd of 0, only we can set that ourselves
- if (!except) {
- debugprint(DEBUG_FULL, "sendtoallclients(): trusting clientfd of 0.\n");
- break;
- }
- if (clients[i].fd == except) {
- // Found client in array, check authentication status
- if (!clients[i].authed) {
- debugprint(DEBUG_SOME, "sendtoallclients(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
- return 0;
- }
- }
- }
-
- // Relay/send to all clients...
- for (int i = 0; i < MAXCLIENTS; i++) {
- // Skip the current client if "except" non-zero (no need to send back to itself)
- if (clients[i].fd == except) {
- continue;
- }
- // ...but only if they are connected...
- if (clients[i].fd > 0) {
- // ...and authenticated
- if (!clients[i].authed) {
- debugprint(DEBUG_SOME, "sendtoallclients(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
- continue;
- }
- debugprint(DEBUG_SOME, "sendtoallclients(): %s: sending '%s' to client with fd %d.\n", sendertype, str, clients[i].fd);
- if (socksend(clients[i].ssl, str, strlen(str), settings->clienttls) == -1) {
- perror("error: sendtoallclients() socksend()\n");
- debugprint(DEBUG_CRIT, "error: sendtoallclients() socksend() error sending to client with fd '%d', errno '%d'.\n", clients[i].fd, errno);
- }
- }
- }
-
- return 1;
-}
-
-// Send whatever string to the real IRC server
-// Client FD and arrays needed to make sure anything relayed from a client is from an authenticated client.
-// clientfd of "0" means trusted, used when we are sending things ourselves that weren't relayed
-// from a real client.
-int sendtoserver(SSL *server_ssl, char *strsrc, int str_len, int clientfd, struct client *clients, struct settings *settings) {
- // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
- char str[MAXDATASIZE];
- strcpy(str, strsrc);
-
- appendcrlf(str); // Do this just before sending so callers don't need to worry about it
- str_len = strlen(str); // Recalculate str_len in case it changed (TODO: so do we even need to pass it to this function?)
-
- // Find the sending client in the clients array and make sure they are authenticated
- for (int i = 0; i < MAXCLIENTS; i++) {
- // Trust clientfd of 0, only we can set that ourselves
- if (!clientfd) {
- debugprint(DEBUG_FULL, "sendtoserver(): trusting clientfd of 0.\n");
- break;
- }
- if (clients[i].fd == clientfd) {
- // Found client in array, check authentication status
- if (!clients[i].authed) {
- debugprint(DEBUG_SOME, "sendtoserver(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
- return 0;
- }
- }
- }
-
- debugprint(DEBUG_SOME, "sendtoserver(): sending '%s' to IRC server (length %d).\n", str, str_len);
- if (socksend(server_ssl, str, str_len, settings->servertls) == -1) {
- printf("error: sendtoserver() socksend()\n");
- debugprint(DEBUG_CRIT, "error: sendtoserver() socksend() error sending to server, errno '%d'.\n", clientfd, errno);
- return 0;
- }
-
- return 1;
-}
-
-// Disconnect the client fd "fd" by close()ing it and remove
-// it from the array of clients.
-// Also set its authentication and registration statuses to 0.
-// Also set the pending statuses to 0
-int disconnectclient(int fd, struct client *clients, struct ircdstrings *ircdstrings, struct settings *settings) {
- debugprint(DEBUG_SOME, "disconnectclient(): disconnecting client fd '%d'\n", fd);
-
- // Alert other clients about the disconnection (don't send yet, we haven't removed from the clients array yet)
- char alertmsg[MAXDATASIZE];
- if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: client with fd %d has disconnected.", ircdstrings->ircnick, fd)) {
- fprintf(stderr, "Error while preparing authentication failure NOTICE!\n");
- debugprint(DEBUG_CRIT, "Error while preparing authentication failure NOTICE!\n");
- alertmsg[0] = '\0';
- }
- // Remove the client from the clients array
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd == fd) {
- debugprint(DEBUG_FULL, "found and clearing fd %d from clients[%d]\n", fd, i);
- clients[i].fd = 0;
- clients[i].authed = 0;
- clients[i].registered = 0;
- clients[i].pendingchannelmode = 0;
- clients[i].pendingban = 0;
- clients[i].pendingwho = 0;
- clients[i].pendinglist = 0;
- clients[i].pendingwhois = 0;
- clients[i].pendingwhowas = 0;
- clients[i].pendingnames = 0;
- clients[i].pendingcap = 0;
- if (settings->clienttls) {
- // Finish up with OpenSSL if using client TLS
- SSL_free(clients[i].ssl);
- }
- // Close the socket
- close(fd);
- // Now clients array is cleared, inform all other clients (source "0" since we trust this message)
- sendtoallclients(clients, alertmsg, 0, settings);
- return 1;
- }
- }
-
- // If we got here, we didn't find and clear the client
- // TODO - Do something with a failed return code
- return 0;
-}
-
-int createchannel(struct channel *channels, 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) {
- perror("error: createchannel(): channel name already exists.\n");
- return 0;
- break;
- }
- }
-
- // 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++) {
- 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;
- return 1;
- break; // TODO - This should be safe to remove since return is hit first
- }
- }
-
- 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) {
- 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++) {
- if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
- strncpy(channels[i].topicwho, who, strlen(who));
- channels[i].topicwho[strlen(who)] = '\0';
- strncpy(channels[i].topicwhen, when, strlen(when));
- channels[i].topicwhen[strlen(when)] = '\0';
- return 1;
- }
- }
-
- // TODO - Make a failed return do something to callers
- return 0;
-}
-
-int setchanneltopic(struct channel *channels, char *channelname, char *topic) {
- debugprint(DEBUG_FULL, "setchanneltopic(): given '%s' and '%s'.\n", channelname, topic);
-
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
- strncpy(channels[i].topic, topic, strlen(topic));
- channels[i].topic[strlen(topic)] = '\0';
- return 1;
- }
- }
-
- // TODO - Make a failed return do something to callers
- return 0;
-}
-
-int getchannelcount(struct channel *channels) {
- int count = 0;
-
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (channels[i].name[0]) {
- count++;
- }
- }
-
- debugprint(DEBUG_FULL, "getchannelcount(): counted %d channels.\n", count);
-
- return count;
-}
-
-int removechannel(struct channel *channels, char *name) {
- debugprint(DEBUG_FULL, "removechannel(): given '%s'.\n", name);
-
- // Clear its topic setter and timestamp...
- setchanneltopicwhotime(channels, name, "", "0");
-
- // Find the channel in the channel array...
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (strncmp(channels[i].name, name, strlen(name)) == 0) {
- // ..and NULL its name (0th character = '\0')
- channels[i].name[0] = '\0';
- debugprint(DEBUG_FULL, "removechannel(): channel '%s' removed and topicwhen set to '%s'.\n", name, channels[i].topicwhen);
- return 1;
- }
- }
-
- debugprint(DEBUG_CRIT, "error: removechannel() didn't remove a channel\n");
-
- // TODO - Make a failed return do something to callers
- return 0;
-}
-
-// 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) {
- debugprint(DEBUG_FULL, "channelgotnames(): given '%s'.\n", name);
- for (int i = 0; i < MAXCHANNELS; 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);
- return 1;
- } else {
- debugprint(DEBUG_FULL, "channelgotnames(): channel '%s' gotnames was not set, returning '%d'.\n", channels[i].name, channels[i].gotnames);
- return 0;
- }
- }
- }
-
- // We didn't find the channel, this isn't good! TODO - Do something if this happens.
- debugprint(DEBUG_CRIT, "channelgotnames(): channel '%s' not found, this is bad, returning -1.\n", name);
- return -1;
-}
-
-// 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) {
- // 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) {
- while (name[strlen(name) - 1] == '\r' || name[strlen(name) - 1] == '\n') {
- name[strlen(name) - 1] = '\0';
- }
- }
-
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (strncmp(channels[i].name, name, strlen(name)) == 0) {
- debugprint(DEBUG_FULL, "inchannel(): in channel '%s'.\n", name);
- return 1;
- }
- }
-
- // We're not in the channel
- debugprint(DEBUG_FULL, "inchannel(): NOT in channel '%s'.\n", name);
- return 0;
-}
-
-// 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) {
- debugprint(DEBUG_FULL, "channelindex(): given '%s'.\n", name);
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (strncmp(channels[i].name, name, strlen(name)) == 0) {
- return i;
- }
- }
-
- // We didn't find the channel, this isn't good! TODO - Do something if this happens.
- debugprint(DEBUG_CRIT, "channelindex(): channel '%s' not found, this is bad, returning -1.\n", name);
- return -1;
-}
-
-// 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
-// seconds of replay to replay.
-// Returns 1 for success or 0 for failure.
-int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstrings *ircdstrings, struct channel *channels) {
- char outgoingmsg[MAXDATASIZE];
-
- // Figure out how many lines to replay
- int numlines = replaylines(replayseconds, settings->basedir);
- debugprint(DEBUG_FULL, "Replay log lines: '%d'.\n", numlines);
-
- if (numlines < 0) {
- debugprint(DEBUG_CRIT, "Error getting number of replay lines.\n");
- return 0;
- } else if (numlines == 0) {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- return 1;
- }
-
- // Announce the start
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Starting log replay....", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- // Replay those lines!
- for (int i = 0; i < numlines; i++) {
- if (!readreplayline(replayseconds, i, outgoingmsg, settings->basedir)) {
- debugprint(DEBUG_CRIT, "Error requesting replay line.\n");
- return 0;
- }
-
- // Check if the replay line is a TOPIC, a JOIN, or a PART so we don't
- // replay those if we are not currently in the channel they are from
- // otherwise clients and state go a bit mad.
- // Never replay them if they are from us.
-
- // Copy to a temporary string
- char *strcopy = strdup(outgoingmsg);
- // Keep track of initial pointer for free()ing later
- char *strcopyPtr = strcopy;
-
- // Build array of each space-separated token
- char tokens[3][MAXDATASIZE];
- char *token;
-
- for (int j = 0; j < 3; j++) {
- // Try to split
- if ((token = strsep(&strcopy, " ")) == NULL) {
- debugprint(DEBUG_CRIT, "doreplay(): error splitting string on iteration '%d', returning!\n", j);
- return 0;
- }
- // Copy into the token array (strlen + 1 to get the NULL terminator)
- strncpy(tokens[j], token, strlen(token) + 1);
- }
-
- if (strncmp(tokens[1], "TOPIC", strlen("TOPIC")) == 0 ||
- strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0 ||
- strncmp(tokens[1], "PART", strlen("PART")) == 0) {
- // Skip over colon if present in channel name
- int offset = 0;
- if (tokens[2][0] == ':') {
- offset = 1;
- }
-
- // To make sure it's not us
- extractnickfromprefix(tokens[0]);
-
- // 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], ircdstrings->ircnick, strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Not sending '%s' replay line '%s'.\n", tokens[1], outgoingmsg);
- free(strcopyPtr);
- continue;
- }
- }
-
- free(strcopyPtr);
-
- debugprint(DEBUG_FULL, "Sending replay line: '%s'.\n", outgoingmsg);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- // Announce the end
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- return 1;
-}
-
-// Return a count of the number of connected clients
-int numclients(struct client *clients) {
- int count = 0;
-
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd) {
- count++;
- }
- }
-
- debugprint(DEBUG_FULL, "numclients(): '%d' clients connected.\n", count);
- return count;
-}
-
-// Join any channels that were configured to be automatically
-// joined in the configuration file.
-// Returns 1 on success or 0 on failure.
-int joinautochannels(SSL *server_ssl, struct client *clients, struct settings *settings) {
- if (strlen(settings->autochannels) == 0) {
- // None configured
- debugprint(DEBUG_FULL, "joinautochannels(): none configured.\n");
- return 1;
- }
-
- // Split string up into each channel
- char tokens[MAXAUTOCHANLEN][MAXCHANLENGTH];
- int counter = 0;
-
- // Copy to a temporary string
- char *strcopy = strdup(settings->autochannels);
- // Keep track of initial pointer for free()ing later
- char *strcopyPtr = strcopy;
-
- char *token;
-
- // Split on commas
- while ((token = strsep(&strcopy, ",")) != NULL) {
- if (*token == '\0') continue; // Skip consecutive matches
- if (counter >= MAXAUTOCHANLEN) break; // Too many tokens
- debugprint(DEBUG_FULL, " >> Auto channel: '%s', length '%ld'.\n", token, strlen(token));
- // Copy into the token array (strlen + 1 to get the NULL terminator)
- strncpy(tokens[counter], token, strlen(token) + 1);
- if (strlen(tokens[counter]) > MAXCHANLENGTH) {
- printf("error: channel name '%s' from configuration file too long, max length is '%d'.\n", tokens[counter], MAXCHANLENGTH);
- debugprint(DEBUG_CRIT, "error: channel name '%s' from configuration file too long, max length is '%d'.\n", tokens[counter], MAXCHANLENGTH);
- exit(1);
- }
- counter++;
- }
-
- // Join all the channels
- for (int i = 0; i < counter; i++) {
- debugprint(DEBUG_FULL, "joinautochannels(): Joining '%s'.\n", tokens[i]);
- char joinmsg[MAXDATASIZE];
- snprintf(joinmsg, MAXDATASIZE, "JOIN %s", tokens[i]);
- sendtoserver(server_ssl, joinmsg, strlen(joinmsg), 0, clients, settings);
- }
-
- free(strcopyPtr);
- // TODO - Can we fail here? Return 0 if so and make callers handle this if so.
- return 1;
-}
-
-// Try to make a new nick if no configured are available or liked by the server
-// Do this by sticking a number on the end of the current nick and trying numbers
-// 1 through to 9.
-void tryautonick(struct ircdstrings *ircdstrings) {
- // Increment the attempts counter
- ircdstrings->autonicknum++;
-
- if (ircdstrings->autonicknum == 10) {
- // We've already tried 9 nicks and failed, give up
- printf("tryautonick(): Tried 9 automatic nicks and the server didn't like any, giving up.\n");
- debugprint(DEBUG_CRIT, "tryautonick(): Tried 9 automatic nicks and the server didn't like any, giving up.\n");
- exit(1);
- }
-
- int oldlen = strlen(ircdstrings->ircnick);
-
- // If we've already started trying autonick, just replace the last character a the new number
- if (ircdstrings->autonicknum > 1) {
- debugprint(DEBUG_FULL, "tryautonick(): already started autonick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
- ircdstrings->ircnick[oldlen - 1] = ircdstrings->autonicknum + '0';
- // And null terminate
- ircdstrings->ircnick[oldlen] = '\0';
- // If the nick is longer than or equal to the RFC 1459 max nick
- // length then try sticking the number at the end
- } else if (oldlen >= MAXRFCNICKLEN) {
- debugprint(DEBUG_FULL, "tryautonick(): long old nick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
- // (+ '0' to make char from int)
- ircdstrings->ircnick[MAXRFCNICKLEN] = ircdstrings->autonicknum + '0';
- // And null terminate
- ircdstrings->ircnick[MAXRFCNICKLEN + 1] = '\0';
- // Otherwise, just stick it on the end (+ '0' to make char from int)
- } else {
- debugprint(DEBUG_FULL, "tryautonick(): short old nick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
- ircdstrings->ircnick[oldlen] = ircdstrings->autonicknum + '0';
- // And null terminate
- ircdstrings->ircnick[oldlen + 1] = '\0';
- }
-
- debugprint(DEBUG_FULL, "tryautonick(): set irdstrings->ircnick to '%s'.\n", ircdstrings->ircnick);
-}
-
int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, struct ircdstrings *ircdstrings, struct settings *settings, struct client *clients) {
char outgoingmsg[MAXDATASIZE]; // String to send to server
diff --git a/functions.c b/functions.c
index b9983d7..0aefa05 100644
--- a/functions.c
+++ b/functions.c
@@ -355,3 +355,560 @@ void updategreetings(char *greeting001, char *greeting002, char *greeting003, ch
updategreetingnick(greeting005c, "005", newnickcpy, oldnick);
}
}
+
+// Return index of requested client FD within the clients array.
+// TODO - Use this wherever we are calculating the position (various places) instead of
+// duplicating code.
+int arrindex(struct client *clients, int clientfd) {
+ // Find the client in the clients array and make sure they are authenticated
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd == clientfd) {
+ return i;
+ }
+ }
+
+ // Something went wrong, we didn't find it
+ return 0;
+}
+
+// Send whatever string to a specific client by providing the FD
+// If "bypass" == 1 then permit sending to client even if unauthenticated (for instance for a CAP LS response)
+int sendtoclient(int fd, char *strsrc, struct client *clients, struct settings *settings, int bypass) {
+ // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
+ char str[MAXDATASIZE];
+ strcpy(str, strsrc);
+
+ appendcrlf(str); // Do this just before sending so callers don't need to worry about it
+
+ int i = 0;
+ // Find the client in the clients array and make sure they are authenticated
+ for (i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd == fd) {
+ // Found client in array, check authentication status
+ if (!clients[i].authed && !bypass) {
+ debugprint(DEBUG_SOME, "sendtoclient(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
+ return 0;
+ }
+ // Break when we get to the correct fd, "i" is now the position in the clients array of our client
+ break;
+ }
+ }
+
+ debugprint(DEBUG_SOME, "sendtoclient(): sending \"%s\" (length %zd) to client with fd %d.\n", str, strlen(str), fd);
+ if (socksend(clients[i].ssl, str, strlen(str), settings->clienttls) == -1) {
+ perror("error: sendtoclient() socksend()\n");
+ debugprint(DEBUG_CRIT, "error: sendtoclient() socksend() error sending to client with fd '%d', errno '%d'.\n", fd, errno);
+ return 0;
+ }
+
+ return 1;
+}
+
+// Relay/send message to all clients (optionally except one)
+// "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all)
+// "except" is really the "sourcefd" and is also used as part of the authentication check - this is messy and they should perhaps be two separate arguments.
+int sendtoallclients(struct client *clients, char *strsrc, int except, struct settings *settings) {
+
+ char *sendertype;
+
+ // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
+ char str[MAXDATASIZE];
+ strcpy(str, strsrc);
+
+ appendcrlf(str); // Do this just before sending so callers don't need to worry about it
+
+ // Decide what sort of text to prefix the debug output with
+ // At the moment if non-zero "except" is specified then it must be a message from a bouncer client
+ // and if "except" is zero then it must be a message from the real IRC server
+ if (except) {
+ sendertype = "bouncer-client";
+ } else {
+ sendertype = "bouncer-server";
+ }
+
+ // Find the sending client in the clients array and make sure they are authenticated
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ // Trust clientfd of 0, only we can set that ourselves
+ if (!except) {
+ debugprint(DEBUG_FULL, "sendtoallclients(): trusting clientfd of 0.\n");
+ break;
+ }
+ if (clients[i].fd == except) {
+ // Found client in array, check authentication status
+ if (!clients[i].authed) {
+ debugprint(DEBUG_SOME, "sendtoallclients(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
+ return 0;
+ }
+ }
+ }
+
+ // Relay/send to all clients...
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ // Skip the current client if "except" non-zero (no need to send back to itself)
+ if (clients[i].fd == except) {
+ continue;
+ }
+ // ...but only if they are connected...
+ if (clients[i].fd > 0) {
+ // ...and authenticated
+ if (!clients[i].authed) {
+ debugprint(DEBUG_SOME, "sendtoallclients(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
+ continue;
+ }
+ debugprint(DEBUG_SOME, "sendtoallclients(): %s: sending '%s' to client with fd %d.\n", sendertype, str, clients[i].fd);
+ if (socksend(clients[i].ssl, str, strlen(str), settings->clienttls) == -1) {
+ perror("error: sendtoallclients() socksend()\n");
+ debugprint(DEBUG_CRIT, "error: sendtoallclients() socksend() error sending to client with fd '%d', errno '%d'.\n", clients[i].fd, errno);
+ }
+ }
+ }
+
+ return 1;
+}
+
+// Send whatever string to the real IRC server
+// Client FD and arrays needed to make sure anything relayed from a client is from an authenticated client.
+// clientfd of "0" means trusted, used when we are sending things ourselves that weren't relayed
+// from a real client.
+int sendtoserver(SSL *server_ssl, char *strsrc, int str_len, int clientfd, struct client *clients, struct settings *settings) {
+ // Copy to new string for passing to appendcrlf() to avoid overrun in appendcrlf()
+ char str[MAXDATASIZE];
+ strcpy(str, strsrc);
+
+ appendcrlf(str); // Do this just before sending so callers don't need to worry about it
+ str_len = strlen(str); // Recalculate str_len in case it changed (TODO: so do we even need to pass it to this function?)
+
+ // Find the sending client in the clients array and make sure they are authenticated
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ // Trust clientfd of 0, only we can set that ourselves
+ if (!clientfd) {
+ debugprint(DEBUG_FULL, "sendtoserver(): trusting clientfd of 0.\n");
+ break;
+ }
+ if (clients[i].fd == clientfd) {
+ // Found client in array, check authentication status
+ if (!clients[i].authed) {
+ debugprint(DEBUG_SOME, "sendtoserver(): skipping unauthenticated client with fd %d.\n", clients[i].fd);
+ return 0;
+ }
+ }
+ }
+
+ debugprint(DEBUG_SOME, "sendtoserver(): sending '%s' to IRC server (length %d).\n", str, str_len);
+ if (socksend(server_ssl, str, str_len, settings->servertls) == -1) {
+ printf("error: sendtoserver() socksend()\n");
+ debugprint(DEBUG_CRIT, "error: sendtoserver() socksend() error sending to server, errno '%d'.\n", clientfd, errno);
+ return 0;
+ }
+
+ return 1;
+}
+
+// Disconnect the client fd "fd" by close()ing it and remove
+// it from the array of clients.
+// Also set its authentication and registration statuses to 0.
+// Also set the pending statuses to 0
+int disconnectclient(int fd, struct client *clients, struct ircdstrings *ircdstrings, struct settings *settings) {
+ debugprint(DEBUG_SOME, "disconnectclient(): disconnecting client fd '%d'\n", fd);
+
+ // Alert other clients about the disconnection (don't send yet, we haven't removed from the clients array yet)
+ char alertmsg[MAXDATASIZE];
+ if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: client with fd %d has disconnected.", ircdstrings->ircnick, fd)) {
+ fprintf(stderr, "Error while preparing authentication failure NOTICE!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing authentication failure NOTICE!\n");
+ alertmsg[0] = '\0';
+ }
+ // Remove the client from the clients array
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd == fd) {
+ debugprint(DEBUG_FULL, "found and clearing fd %d from clients[%d]\n", fd, i);
+ clients[i].fd = 0;
+ clients[i].authed = 0;
+ clients[i].registered = 0;
+ clients[i].pendingchannelmode = 0;
+ clients[i].pendingban = 0;
+ clients[i].pendingwho = 0;
+ clients[i].pendinglist = 0;
+ clients[i].pendingwhois = 0;
+ clients[i].pendingwhowas = 0;
+ clients[i].pendingnames = 0;
+ clients[i].pendingcap = 0;
+ if (settings->clienttls) {
+ // Finish up with OpenSSL if using client TLS
+ SSL_free(clients[i].ssl);
+ }
+ // Close the socket
+ close(fd);
+ // Now clients array is cleared, inform all other clients (source "0" since we trust this message)
+ sendtoallclients(clients, alertmsg, 0, settings);
+ return 1;
+ }
+ }
+
+ // If we got here, we didn't find and clear the client
+ // TODO - Do something with a failed return code
+ return 0;
+}
+
+int createchannel(struct channel *channels, 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) {
+ perror("error: createchannel(): channel name already exists.\n");
+ return 0;
+ break;
+ }
+ }
+
+ // 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++) {
+ 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;
+ return 1;
+ break; // TODO - This should be safe to remove since return is hit first
+ }
+ }
+
+ 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) {
+ 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++) {
+ if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
+ strncpy(channels[i].topicwho, who, strlen(who));
+ channels[i].topicwho[strlen(who)] = '\0';
+ strncpy(channels[i].topicwhen, when, strlen(when));
+ channels[i].topicwhen[strlen(when)] = '\0';
+ return 1;
+ }
+ }
+
+ // TODO - Make a failed return do something to callers
+ return 0;
+}
+
+int setchanneltopic(struct channel *channels, char *channelname, char *topic) {
+ debugprint(DEBUG_FULL, "setchanneltopic(): given '%s' and '%s'.\n", channelname, topic);
+
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
+ strncpy(channels[i].topic, topic, strlen(topic));
+ channels[i].topic[strlen(topic)] = '\0';
+ return 1;
+ }
+ }
+
+ // TODO - Make a failed return do something to callers
+ return 0;
+}
+
+int getchannelcount(struct channel *channels) {
+ int count = 0;
+
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (channels[i].name[0]) {
+ count++;
+ }
+ }
+
+ debugprint(DEBUG_FULL, "getchannelcount(): counted %d channels.\n", count);
+
+ return count;
+}
+
+int removechannel(struct channel *channels, char *name) {
+ debugprint(DEBUG_FULL, "removechannel(): given '%s'.\n", name);
+
+ // Clear its topic setter and timestamp...
+ setchanneltopicwhotime(channels, name, "", "0");
+
+ // Find the channel in the channel array...
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, name, strlen(name)) == 0) {
+ // ..and NULL its name (0th character = '\0')
+ channels[i].name[0] = '\0';
+ debugprint(DEBUG_FULL, "removechannel(): channel '%s' removed and topicwhen set to '%s'.\n", name, channels[i].topicwhen);
+ return 1;
+ }
+ }
+
+ debugprint(DEBUG_CRIT, "error: removechannel() didn't remove a channel\n");
+
+ // TODO - Make a failed return do something to callers
+ return 0;
+}
+
+// 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) {
+ debugprint(DEBUG_FULL, "channelgotnames(): given '%s'.\n", name);
+ for (int i = 0; i < MAXCHANNELS; 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);
+ return 1;
+ } else {
+ debugprint(DEBUG_FULL, "channelgotnames(): channel '%s' gotnames was not set, returning '%d'.\n", channels[i].name, channels[i].gotnames);
+ return 0;
+ }
+ }
+ }
+
+ // We didn't find the channel, this isn't good! TODO - Do something if this happens.
+ debugprint(DEBUG_CRIT, "channelgotnames(): channel '%s' not found, this is bad, returning -1.\n", name);
+ return -1;
+}
+
+// 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) {
+ // 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) {
+ while (name[strlen(name) - 1] == '\r' || name[strlen(name) - 1] == '\n') {
+ name[strlen(name) - 1] = '\0';
+ }
+ }
+
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, name, strlen(name)) == 0) {
+ debugprint(DEBUG_FULL, "inchannel(): in channel '%s'.\n", name);
+ return 1;
+ }
+ }
+
+ // We're not in the channel
+ debugprint(DEBUG_FULL, "inchannel(): NOT in channel '%s'.\n", name);
+ return 0;
+}
+
+// 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) {
+ debugprint(DEBUG_FULL, "channelindex(): given '%s'.\n", name);
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, name, strlen(name)) == 0) {
+ return i;
+ }
+ }
+
+ // We didn't find the channel, this isn't good! TODO - Do something if this happens.
+ debugprint(DEBUG_CRIT, "channelindex(): channel '%s' not found, this is bad, returning -1.\n", name);
+ return -1;
+}
+
+// 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
+// seconds of replay to replay.
+// Returns 1 for success or 0 for failure.
+int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstrings *ircdstrings, struct channel *channels) {
+ char outgoingmsg[MAXDATASIZE];
+
+ // Figure out how many lines to replay
+ int numlines = replaylines(replayseconds, settings->basedir);
+ debugprint(DEBUG_FULL, "Replay log lines: '%d'.\n", numlines);
+
+ if (numlines < 0) {
+ debugprint(DEBUG_CRIT, "Error getting number of replay lines.\n");
+ return 0;
+ } else if (numlines == 0) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ return 1;
+ }
+
+ // Announce the start
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Starting log replay....", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Replay those lines!
+ for (int i = 0; i < numlines; i++) {
+ if (!readreplayline(replayseconds, i, outgoingmsg, settings->basedir)) {
+ debugprint(DEBUG_CRIT, "Error requesting replay line.\n");
+ return 0;
+ }
+
+ // Check if the replay line is a TOPIC, a JOIN, or a PART so we don't
+ // replay those if we are not currently in the channel they are from
+ // otherwise clients and state go a bit mad.
+ // Never replay them if they are from us.
+
+ // Copy to a temporary string
+ char *strcopy = strdup(outgoingmsg);
+ // Keep track of initial pointer for free()ing later
+ char *strcopyPtr = strcopy;
+
+ // Build array of each space-separated token
+ char tokens[3][MAXDATASIZE];
+ char *token;
+
+ for (int j = 0; j < 3; j++) {
+ // Try to split
+ if ((token = strsep(&strcopy, " ")) == NULL) {
+ debugprint(DEBUG_CRIT, "doreplay(): error splitting string on iteration '%d', returning!\n", j);
+ return 0;
+ }
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(tokens[j], token, strlen(token) + 1);
+ }
+
+ if (strncmp(tokens[1], "TOPIC", strlen("TOPIC")) == 0 ||
+ strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0 ||
+ strncmp(tokens[1], "PART", strlen("PART")) == 0) {
+ // Skip over colon if present in channel name
+ int offset = 0;
+ if (tokens[2][0] == ':') {
+ offset = 1;
+ }
+
+ // To make sure it's not us
+ extractnickfromprefix(tokens[0]);
+
+ // 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], ircdstrings->ircnick, strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Not sending '%s' replay line '%s'.\n", tokens[1], outgoingmsg);
+ free(strcopyPtr);
+ continue;
+ }
+ }
+
+ free(strcopyPtr);
+
+ debugprint(DEBUG_FULL, "Sending replay line: '%s'.\n", outgoingmsg);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ // Announce the end
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ return 1;
+}
+
+// Return a count of the number of connected clients
+int numclients(struct client *clients) {
+ int count = 0;
+
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd) {
+ count++;
+ }
+ }
+
+ debugprint(DEBUG_FULL, "numclients(): '%d' clients connected.\n", count);
+ return count;
+}
+
+// Join any channels that were configured to be automatically
+// joined in the configuration file.
+// Returns 1 on success or 0 on failure.
+int joinautochannels(SSL *server_ssl, struct client *clients, struct settings *settings) {
+ if (strlen(settings->autochannels) == 0) {
+ // None configured
+ debugprint(DEBUG_FULL, "joinautochannels(): none configured.\n");
+ return 1;
+ }
+
+ // Split string up into each channel
+ char tokens[MAXAUTOCHANLEN][MAXCHANLENGTH];
+ int counter = 0;
+
+ // Copy to a temporary string
+ char *strcopy = strdup(settings->autochannels);
+ // Keep track of initial pointer for free()ing later
+ char *strcopyPtr = strcopy;
+
+ char *token;
+
+ // Split on commas
+ while ((token = strsep(&strcopy, ",")) != NULL) {
+ if (*token == '\0') continue; // Skip consecutive matches
+ if (counter >= MAXAUTOCHANLEN) break; // Too many tokens
+ debugprint(DEBUG_FULL, " >> Auto channel: '%s', length '%ld'.\n", token, strlen(token));
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(tokens[counter], token, strlen(token) + 1);
+ if (strlen(tokens[counter]) > MAXCHANLENGTH) {
+ printf("error: channel name '%s' from configuration file too long, max length is '%d'.\n", tokens[counter], MAXCHANLENGTH);
+ debugprint(DEBUG_CRIT, "error: channel name '%s' from configuration file too long, max length is '%d'.\n", tokens[counter], MAXCHANLENGTH);
+ exit(1);
+ }
+ counter++;
+ }
+
+ // Join all the channels
+ for (int i = 0; i < counter; i++) {
+ debugprint(DEBUG_FULL, "joinautochannels(): Joining '%s'.\n", tokens[i]);
+ char joinmsg[MAXDATASIZE];
+ snprintf(joinmsg, MAXDATASIZE, "JOIN %s", tokens[i]);
+ sendtoserver(server_ssl, joinmsg, strlen(joinmsg), 0, clients, settings);
+ }
+
+ free(strcopyPtr);
+ // TODO - Can we fail here? Return 0 if so and make callers handle this if so.
+ return 1;
+}
+
+// Try to make a new nick if no configured are available or liked by the server
+// Do this by sticking a number on the end of the current nick and trying numbers
+// 1 through to 9.
+void tryautonick(struct ircdstrings *ircdstrings) {
+ // Increment the attempts counter
+ ircdstrings->autonicknum++;
+
+ if (ircdstrings->autonicknum == 10) {
+ // We've already tried 9 nicks and failed, give up
+ printf("tryautonick(): Tried 9 automatic nicks and the server didn't like any, giving up.\n");
+ debugprint(DEBUG_CRIT, "tryautonick(): Tried 9 automatic nicks and the server didn't like any, giving up.\n");
+ exit(1);
+ }
+
+ int oldlen = strlen(ircdstrings->ircnick);
+
+ // If we've already started trying autonick, just replace the last character a the new number
+ if (ircdstrings->autonicknum > 1) {
+ debugprint(DEBUG_FULL, "tryautonick(): already started autonick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
+ ircdstrings->ircnick[oldlen - 1] = ircdstrings->autonicknum + '0';
+ // And null terminate
+ ircdstrings->ircnick[oldlen] = '\0';
+ // If the nick is longer than or equal to the RFC 1459 max nick
+ // length then try sticking the number at the end
+ } else if (oldlen >= MAXRFCNICKLEN) {
+ debugprint(DEBUG_FULL, "tryautonick(): long old nick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
+ // (+ '0' to make char from int)
+ ircdstrings->ircnick[MAXRFCNICKLEN] = ircdstrings->autonicknum + '0';
+ // And null terminate
+ ircdstrings->ircnick[MAXRFCNICKLEN + 1] = '\0';
+ // Otherwise, just stick it on the end (+ '0' to make char from int)
+ } else {
+ debugprint(DEBUG_FULL, "tryautonick(): short old nick, starting with '%s' length '%ld'.\n", ircdstrings->ircnick, strlen(ircdstrings->ircnick));
+ ircdstrings->ircnick[oldlen] = ircdstrings->autonicknum + '0';
+ // And null terminate
+ ircdstrings->ircnick[oldlen + 1] = '\0';
+ }
+
+ debugprint(DEBUG_FULL, "tryautonick(): set irdstrings->ircnick to '%s'.\n", ircdstrings->ircnick);
+}
diff --git a/functions.h b/functions.h
index d62194d..cba1d72 100644
--- a/functions.h
+++ b/functions.h
@@ -32,7 +32,14 @@
#include <stdarg.h>
#include <limits.h>
-#define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator)
+#include "sockets.h"
+#include "structures.h"
+#include "replay.h"
+
+#define MAXDATASIZE 513 // Maximum number of bytes we can get at once (RFC2812 says 512, plus one for null terminator)
+#define MAXCLIENTS 32 // Maximum number of clients that can connect to the bouncer at a time
+#define MAXCHANNELS 1024 // Let's assume 1024 is reasonable for now (it's configured per IRCd)
+#define MAXRFCNICKLEN 9 // From RFC 1459
// getstdin() return codes
#define OK 0
@@ -69,4 +76,72 @@ void updatenickuserhost(char *nickuserhost, char *nick);
// Update an existing 001 greeting with a new nickuserhost
void updategreetings(char *greeting001, char *greeting002, char *greeting003, char *greeting004, char *greeting005a, char *greeting005b, char *greeting005c, char *newnickuserhost, char *oldnickuserhost, char *newnick, char *oldnick);
+// Return index of requested client FD within the clients array.
+// TODO - Use this wherever we are calculating the position (various places) instead of
+// duplicating code.
+int arrindex(struct client *clients, int clientfd);
+
+// Send whatever string to a specific client by providing the FD
+// If "bypass" == 1 then permit sending to client even if unauthenticated (for instance for a CAP LS response)
+int sendtoclient(int fd, char *strsrc, struct client *clients, struct settings *settings, int bypass);
+
+// Relay/send message to all clients (optionally except one)
+// "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all)
+// "except" is really the "sourcefd" and is also used as part of the authentication check - this is messy and they should perhaps be two separate arguments.
+int sendtoallclients(struct client *clients, char *strsrc, int except, struct settings *settings);
+
+// Send whatever string to the real IRC server
+// Client FD and arrays needed to make sure anything relayed from a client is from an authenticated client.
+// clientfd of "0" means trusted, used when we are sending things ourselves that weren't relayed
+// from a real client.
+int sendtoserver(SSL *server_ssl, char *strsrc, int str_len, int clientfd, struct client *clients, struct settings *settings);
+
+// Disconnect the client fd "fd" by close()ing it and remove
+// it from the array of clients.
+// Also set its authentication and registration statuses to 0.
+// Also set the pending statuses to 0
+int disconnectclient(int fd, struct client *clients, struct ircdstrings *ircdstrings, struct settings *settings);
+
+int createchannel(struct channel *channels, char *name, char *topic, char *topicwho, char *topicwhen);
+
+int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when);
+
+int setchanneltopic(struct channel *channels, char *channelname, char *topic);
+
+int getchannelcount(struct channel *channels);
+
+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);
+
+// 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);
+
+// 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);
+
+// 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
+// seconds of replay to replay.
+// Returns 1 for success or 0 for failure.
+int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstrings *ircdstrings, struct channel *channels);
+
+// Return a count of the number of connected clients
+int numclients(struct client *clients);
+
+// Join any channels that were configured to be automatically
+// joined in the configuration file.
+// Returns 1 on success or 0 on failure.
+int joinautochannels(SSL *server_ssl, struct client *clients, struct settings *settings);
+
+// Try to make a new nick if no configured are available or liked by the server
+// Do this by sticking a number on the end of the current nick and trying numbers
+// 1 through to 9.
+void tryautonick(struct ircdstrings *ircdstrings);
+
#endif