From 5d21a0eb5bd7deed931078a302ca3b8d6881e62e Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Mon, 10 Jun 2019 20:35:05 +0100 Subject: Refactoring - move most functions into functions.h/functions.c except connect/exit functions only relevant in main.c. --- blabouncer.c | 563 +---------------------------------------------------------- functions.c | 557 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ functions.h | 77 +++++++- 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 #include -#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 -- cgit v1.2.3