summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-06-10 21:17:10 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-06-10 21:17:10 +0100
commitc84d8c493ccdc840a866a9f51f10fe6b1f2bc377 (patch)
treeb064e1f554b19378beb0909c78061001b331eb3a
parent853efa64ff8ad6ebde47d1265f8b13e954a0d2f0 (diff)
Refactoring - split giant processircmessage() switch statement into separate server and client functions in message.h/message.c.
-rw-r--r--Makefile6
-rw-r--r--blabouncer.c1313
-rw-r--r--functions.c24
-rw-r--r--functions.h17
-rw-r--r--message.c1264
-rw-r--r--message.h37
6 files changed, 1346 insertions, 1315 deletions
diff --git a/Makefile b/Makefile
index 3316e82..caa293d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
CC=gcc
-DEPS = functions.h sockets.h config.h replay.h logging.h
+DEPS = functions.h sockets.h config.h replay.h logging.h structures.h message.h
%.o: %.c $(DEPS)
$(CC) -Wall -Wextra -c -o $@ $<
-blabouncer: blabouncer.o functions.o sockets.o config.o replay.o logging.o
- $(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o logging.o
+blabouncer: blabouncer.o functions.o sockets.o config.o replay.o logging.o message.o
+ $(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o logging.o message.o
diff --git a/blabouncer.c b/blabouncer.c
index 438107b..bf27835 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -46,6 +46,7 @@
#include "replay.h"
#include "logging.h"
#include "structures.h"
+#include "message.h"
#define SOURCE_SERVER 0
#define SOURCE_CLIENT 1
@@ -154,30 +155,6 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd,
return 1; // TODO - Return 0 if this fails and make callers do something with that
}
-// Exit the program cleanly - tell clients, tell the server, then exit(0)
-// Optional quit message string "quitmsg"
-// "sourcefd" of 0 means the request didn't come from a client
-void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings, struct settings *settings, char *quitmsg) {
- char outgoingmsg[MAXDATASIZE];
-
- // Tell clients and debug log
- if (sourcefd) {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Exiting on request from client with fd '%d', message '%s'.", ircdstrings->ircnick, sourcefd, quitmsg);
- debugprint(DEBUG_CRIT, "Exiting on request from client with fd '%d', message '%s'.\n", sourcefd, quitmsg);
- } else {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Exiting on request (not from a client), message '%s'.", ircdstrings->ircnick, quitmsg);
- debugprint(DEBUG_CRIT, "Exiting on request (not from a client), message '%s'.\n", quitmsg);
- }
- sendtoallclients(clients, outgoingmsg, EXCEPT_NONE, settings);
-
- // Tell the server
- // Combine "QUIT :" with any (optional) quit message
- snprintf(outgoingmsg, MAXDATASIZE, "QUIT :%s", quitmsg);
- sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), sourcefd, clients, settings);
-
- exit(0);
-}
-
// Figure out what to do with each CRLF-split IRC message (if anything)
// by splitting out the different components by space character (ASCII 0x20).
//
@@ -222,1300 +199,22 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
switch(source) {
case SOURCE_SERVER: // If message(s) were from the real IRC server
- // Record that we received something from the server for timeout checking purposes
- ircdstrings->lastmessagetime = time(NULL); // snprintf(NULL, 0, "%ld", timenow);
-
- // Server PING received? If so, send a PONG back with the next element as the argument.
- if (strncmp(tokens[0], "PING", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Server PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0]));
-
- char outgoingmsg[MAXDATASIZE]; // String to send to server
- if (!snprintf(outgoingmsg, MAXDATASIZE, "PONG %s", tokens[1])) { // TODO - Make sure tokens[1] actually has a token
- fprintf(stderr, "Error while preparing PONG response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing PONG response!\n");
- outgoingmsg[0] = '\0';
- }
- // sourcefd = 0 as this is a trusted response
- sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
-
+ if (processservermessage(server_ssl, str, clients, sourcefd, ircdstrings, channels, settings, tokens, counter)) {
// We processed something so return true
free(strcopyPtr);
return 1;
}
- // Prefix received? TODO - Care about what the prefix is - what if it's a different server/network/whatever?
- if (tokens[0][0] == ':') {
- debugprint(DEBUG_FULL, "Prefix found: '%s'! Next token is '%s', length %zd.\n", tokens[0], tokens[1], strlen(tokens[1]));
- // Greetings 001 through to 005, store in ircdstrings array for resending when clients connect
- // Also store our nick!user@host from greeting 001
- // Also store the real IRCd's name from greeting 004
- if (strncmp(tokens[1], "001", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Found greeting 001 (%s) (length %zd), storing in ircdstrings struct.\n", str, strlen(str));
- strncpy(ircdstrings->greeting001, str, strlen(str));
- // Null the end of the string
- ircdstrings->greeting001[strlen(str)] = '\0';
- debugprint(DEBUG_FULL, "Storing our nick!user@host (:%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]);
- // Prepend a colon (:) first since everything (so far) needs one
- if (!snprintf(ircdstrings->nickuserhost, MAXDATASIZE, ":%s", tokens[counter - 1])) {
- fprintf(stderr, "Error while preparing nickuserhost for storage!\n");
- debugprint(DEBUG_CRIT, "Error while preparing nickuserhost for storage!\n");
- exit(1);
- }
- // Null the end of the new string
- ircdstrings->nickuserhost[strlen(tokens[counter - 1]) + 1] = '\0'; // +1 for the inserted colon
- debugprint(DEBUG_FULL, "nickuserhost '%s' stored.\n", ircdstrings->nickuserhost);
- free(strcopyPtr);
- return 1;
- } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Found greeting 002 (%s), storing in ircdstrings struct.\n", str);
- strncpy(ircdstrings->greeting002, str, strlen(str));
- // Null the end of the string
- ircdstrings->greeting002[strlen(str)] = '\0';
- free(strcopyPtr);
- return 1;
- } else if (strncmp(tokens[1], "003", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Found greeting 003 (%s), storing in ircdstrings struct.\n", str);
- strncpy(ircdstrings->greeting003, str, strlen(str));
- // Null the end of the string
- ircdstrings->greeting003[strlen(str)] = '\0';
- free(strcopyPtr);
- return 1;
- } else if (strncmp(tokens[1], "004", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Found greeting 004 (%s), storing in ircdstrings struct.\n", str);
- strncpy(ircdstrings->greeting004, str, strlen(str));
- // Null the end of the string
- ircdstrings->greeting004[strlen(str)] = '\0';
- debugprint(DEBUG_FULL, "Storing the real IRCd's name (%s) from greeting 004 in ircdstrings struct.\n", tokens[3]);
- strncpy(ircdstrings->ircdname, tokens[3], strlen(tokens[3]));
- // Null the end of the string
- ircdstrings->ircdname[strlen(tokens[3])] = '\0';
- // Receiving greeting 004 means we're now registered
- // Request IRCv3 multi-prefix extension so we can more accurately inform new clients about current user prefixes
- sendtoserver(server_ssl, "CAP REQ multi-prefix", strlen("CAP REQ multi-prefix"), 0, clients, settings);
- // Send the connect command, if set
- if (settings->connectcommand[0]) {
- sendtoserver(server_ssl, settings->connectcommand, strlen(settings->connectcommand), 0, clients, settings);
- }
- // If this is a reconnection, JOIN existing channels and catch clients up again
- if (ircdstrings->reconnecting) {
- // First tell clients if our nick changed
- if (!strcmp(ircdstrings->ircnick, ircdstrings->oldnick) == 0) {
- debugprint(DEBUG_SOME, "Telling clients about nick change.\n");
- char nickmsg[MAXDATASIZE];
- snprintf(nickmsg, MAXDATASIZE, ":%s NICK :%s", ircdstrings->oldnick, ircdstrings->ircnick);
- sendtoallclients(clients, nickmsg, sourcefd, settings);
- }
-
- // Next re-join channels
- // Storing separately so we can skip over blank channels.
- int channelcount = getchannelcount(channels);
- // Join all the channels and make a note of the channel count
- for (int i = 0; i < channelcount; i++) {
- // Skip this one and increment channelcount if it's a blank channel
- if (!channels[i].name[0]) {
- debugprint(DEBUG_FULL, "Reconnection: Skipping channel[%d], incrementing channelcount.\n", i);
- channelcount++;
- continue;
- }
-
- debugprint(DEBUG_SOME, "Reconnection: Re-joining '%s'.\n", channels[i].name);
-
- char joinmsg[MAXDATASIZE];
- snprintf(joinmsg, MAXDATASIZE, "JOIN %s", channels[i].name);
- sendtoserver(server_ssl, joinmsg, strlen(joinmsg), 0, clients, settings);
- }
-
- // Finally do a replay for all clients and tell them we're reconnected
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd) {
- char alertmsg[MAXDATASIZE];
- if (!doreplay(clients[i].fd, settings->replayseconds, clients, settings, ircdstrings, channels)) {
- snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
- sendtoclient(sourcefd, alertmsg, clients, settings, 0);
- }
- snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Reconnection complete.", ircdstrings->ircnick);
- sendtoclient(clients[i].fd, alertmsg, clients, settings, 0);
- }
- }
-
- // Reconnection complete
- ircdstrings->oldnick[0] = '\0';
- ircdstrings->reconnecting = 0;
- // If it's not, deal with auto channels
- } else {
- // Join any auto channels set in the configuration file
- joinautochannels(server_ssl, clients, settings);
- }
- free(strcopyPtr);
- return 1;
- } else if (strncmp(tokens[1], "005", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Found greeting 005 (%s), storing in ircdstrings struct.\n", str);
- // Find an empty greeting005 string in ircdstrings and store in there...
- if (!ircdstrings->greeting005a[0]) {
- strncpy(ircdstrings->greeting005a, str, strlen(str));
- ircdstrings->greeting005a[strlen(str)] = '\0';
- } else if (!ircdstrings->greeting005b[0]) {
- strncpy(ircdstrings->greeting005b, str, strlen(str));
- ircdstrings->greeting005b[strlen(str)] = '\0';
- } else if (!ircdstrings->greeting005c[0]) {
- strncpy(ircdstrings->greeting005c, str, strlen(str));
- ircdstrings->greeting005c[strlen(str)] = '\0';
- } else {
- // ...or if they are all fill, discard - TODO - Support more than three greeting005 strings!
- debugprint(DEBUG_CRIT, "Already stored three greeting 005 strings, discarding this one.\n");
- }
- free(strcopyPtr);
- return 1;
- }
-
- // Server JOIN received? Add to our local channel array if it's us, or record the user in the channel if it's not us.
- if (strncmp(tokens[1], "JOIN", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server JOIN found and it is: %s with length %zd! Next token is '%s'. Adding to local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]);
- // Next token should be the channel name but it probably needs the leading ':' stripping
- debugprint(DEBUG_FULL, "processircmessage(): Channel name was '%s'\n", tokens[2]);
- stripprefix(tokens[2]);
- debugprint(DEBUG_FULL, "processircmessage(): Channel name now '%s'\n", tokens[2]);
-
- // If the user JOINing is us, then we must have joined a channel, so add to our local channel array.
- // Copy to a temporary string so we still have the original in case we need it
- char *prefixcopy = strdup(tokens[0]);
- // Just get the nick for comparison
- extractnickfromprefix(prefixcopy);
- if (strncmp(prefixcopy, ircdstrings->ircnick, strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Server JOIN: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
- // TODO - Saner way to initialise this since we don't have the variables yet?
- // TODO - Defaulting to type '=' which is "public" since I don't know what else to guess.
- createchannel(channels, tokens[2], "TOPIC", "TOPICWHO", "0");
- } else {
- debugprint(DEBUG_FULL, "Server JOIN: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
- }
-
- // And then send to all clients
- sendtoallclients(clients, str, sourcefd, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(str, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(str, settings->ircnick, settings->basedir, LOG_JOINPART);
- }
-
- free(prefixcopy);
- free(strcopyPtr);
- return 1;
- }
-
- // Server PART received? Remove from our local channel list if it's us.
- if (strncmp(tokens[1], "PART", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server PART found and it is: %s with length %zd! Next token is '%s'. Removing from local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]);
-
- // If the user PARTing is us, then we must have left a channel, so remove it from our local channel array.
- // (If it's not us, then it's another user PARTing a channel, so just pass straight through to letting all our clients know.)
- // Copy to a temporary string so we still have the original in case we need it
- char *prefixcopy = strdup(tokens[0]);
- // Just get the nick for comparison
- extractnickfromprefix(prefixcopy);
- if (strncmp(prefixcopy, ircdstrings->ircnick, strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Server PART: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
- removechannel(channels, tokens[2]);
- } else {
- debugprint(DEBUG_FULL, "Server PART: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
- }
-
- // And then send to all clients
- sendtoallclients(clients, str, sourcefd, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(str, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(str, settings->ircnick, settings->basedir, LOG_JOINPART);
- }
-
- free(prefixcopy);
- free(strcopyPtr);
- return 1;
- }
-
- // Channel topics/names/nicks/etc.
- // Server 331 (RPL_NOTOPIC) the topic is blank which we track by having a set timestamp of 0
- if (strncmp(tokens[1], "331", strlen(tokens[1])) == 0) {
- // Might as well blank our current topic value
- setchanneltopic(channels, tokens[3], "");
- // Set the topic timestamp to 0 which we use to determine an "unset" topic when new clients connect
- setchanneltopicwhotime(channels, tokens[3], "", "0");
- // Server 332 (RPL_TOPIC) set the channel topic
- } else if (strncmp(tokens[1], "332", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 332 (RPL_TOPIC) found, extracting topic and storing in channel struct.\n");
- // Need to extract the final parameter as topics can have spaces
- // Copy to a temporary string so we still have the original in case we need it
- char *topiccopy = strdup(str);
- extractfinalparameter(topiccopy);
- setchanneltopic(channels, tokens[3], topiccopy);
- free(topiccopy);
- // Server 333 (RPL_TOPICWHOTIME) set the channel topic setter and the time it was set
- } else if (strncmp(tokens[1], "333", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 333 (RPL_TOPICWHOTIME) found, extracting who and when, and storing in channel struct.\n");
- setchanneltopicwhotime(channels, tokens[3], tokens[4], tokens[5]);
- // Server 353 (RPL_NAMREPLY), relay to all clients if we've just JOINed the channel, or relay to any clients
- // who were pending RPL_NAMREPLYs if it's an existing channel.
- } else if (strncmp(tokens[1], "353", strlen(tokens[1])) == 0) {
- // It must be a new channel and we don't have the NAMES
- if (!channelgotnames(channels, tokens[4])) {
- debugprint(DEBUG_FULL, "Server 353 received for a new channel, sending to all clients.\n");
- // Relay to all clients
- sendtoallclients(clients, str, sourcefd, settings);
- } else {
- // We were already in the channel and have the NAMES
- debugprint(DEBUG_FULL, "Server 353 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs.\n");
- // If any clients were pending RPL_NAMREPLYs, send this on to them
- // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingnames > 0) {
- debugprint(DEBUG_FULL, "Sending 353 RPL_NAMREPLY for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n", tokens[4], clients[i].fd, clients[i].pendingnames);
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- }
- }
- }
- free(strcopyPtr);
- return 1;
- // Server 366 (RPL_ENDOFNAMES), relay to all clients if we've just JOINed the channel, or relay to
- // and decrement from any clients who were waiting on RPL_NAMREPLY if it's an existing channel.
- } else if (strncmp(tokens[1], "366", strlen(tokens[1])) == 0) {
- int channelelement;
- // It must be a new channel and we don't have the NAMES
- if (!(channelelement = channelgotnames(channels, tokens[3]))) {
- debugprint(DEBUG_FULL, "Server 366 received for a new channel, sending to all clients and set as got names.\n");
- // We have the names now!
- channels[channelindex(channels, tokens[3])].gotnames = 1;
- // Relay to all clients
- sendtoallclients(clients, str, sourcefd, settings);
- } else {
- // We were already in the channel and have the NAMES
- // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
- debugprint(DEBUG_FULL, "Server 366 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs and decrementing their pendingnames count.\n");
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingnames > 0) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And decrement their pendingnames count
- debugprint(DEBUG_FULL, "Decrementing pendingnames due 366 RPL_ENDOFNAMES to for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n",
- tokens[3], clients[i].fd, clients[i].pendingnames);
- clients[i].pendingnames--;
- debugprint(DEBUG_FULL, "Client with fd '%d' has '%d' pendingnames left.\n", clients[i].fd, clients[i].pendingnames);
- }
- }
- }
- free(strcopyPtr);
- return 1;
- }
-
- // Server TOPIC received? Update our local channel topic info then relay to clients.
- if (strncmp(tokens[1], "TOPIC", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server TOPIC found and it is: %s with length %zd! Next token is '%s'. Updating our local channel topic info.\n",
- tokens[0], strlen(tokens[0]), tokens[2]);
-
- // Set the topic itself
-
- // Need to extract the final parameter as topics can have spaces
- // Copy to a temporary string so we still have the original in case we need it
- char *topiccopy = strdup(str);
- extractfinalparameter(topiccopy);
- setchanneltopic(channels, tokens[2], topiccopy);
-
- // Extract the author and get the current timestamp
-
- // Extract the topic setter from the prefix (Prefix of ":foo!bar@baz" means "foo" set the topic.)
- // Copy to a temporary string so we still have the original in case we need it
- char *prefixcopy = strdup(tokens[0]);
- extractnickfromprefix(prefixcopy);
-
- // Get the current time and manipulate it into a C string
- time_t timenow = time(NULL);
- int timenowlen = snprintf(NULL, 0, "%ld", timenow);
- char timenowstr[timenowlen + 1]; // TODO - Make this Year 2038 proof.
- snprintf(timenowstr, timenowlen + 1, "%ld", timenow);
-
- // Actually set the author and timestamp
- setchanneltopicwhotime(channels, tokens[2], prefixcopy, timenowstr);
-
- // And then finally relay to all clients
- sendtoallclients(clients, str, sourcefd, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(str, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(str, settings->ircnick, settings->basedir, LOG_TOPIC);
- }
-
- free(topiccopy);
- free(prefixcopy);
- free(strcopyPtr);
- return 1;
- }
-
- // Server PRIVMSG received? Relay to all clients and write to replay log.
- if (strncmp(tokens[1], "PRIVMSG", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server PRIVMSG found and it is: %s with length %zd! Next token is '%s'. Relaying to all clients.\n",
- tokens[0], strlen(tokens[0]), tokens[2]);
-
- sendtoallclients(clients, str, sourcefd, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(str, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(str, settings->ircnick, settings->basedir, LOG_PRIVMSG);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server NICK received?
- // 1. Find out if it was us and change ircnick and nickuserhost if so
- // 2. Either way, relay to all clients
- if (strncmp(tokens[1], "NICK", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server NICK found and it is: %s with length %zd! Next token is '%s'. Updating records and relaying to all clients.\n",
- tokens[0], strlen(tokens[0]), tokens[2]);
-
- // Was it us?
- // Copy to a temporary string so we still have the original in case we need it
- char *svrprefixcopy = strdup(tokens[0]);
- // Just get the nick for comparison
- extractnickfromprefix(svrprefixcopy);
- if (strncmp(ircdstrings->ircnick, svrprefixcopy, strlen(ircdstrings->ircnick)) == 0) {
- debugprint(DEBUG_FULL, "Server NICK: nick is ours ('%s' vs '%s').\n", svrprefixcopy, ircdstrings->ircnick);
- // Make a copy of the old nickuserhost for updategreetings() below
- char *nickuserhostcpy = strdup(ircdstrings->nickuserhost);
- // Update nickuserhost with the new :nick!user@host
- updatenickuserhost(ircdstrings->nickuserhost, tokens[2]);
- debugprint(DEBUG_FULL, "Updated nickuserhost to '%s'.\n", ircdstrings->nickuserhost);
- // Prepare to update ircnick and greetings strings
- // Temporary copy of new nickuserhost
- char *prefixcopy = strdup(ircdstrings->nickuserhost);
- // Get nick from it
- extractnickfromprefix(prefixcopy);
- // Update greeting strings for relaying to new clients
- updategreetings(ircdstrings->greeting001, ircdstrings->greeting002, ircdstrings->greeting003, ircdstrings->greeting004,
- ircdstrings->greeting005a, ircdstrings->greeting005b, ircdstrings->greeting005c, ircdstrings->nickuserhost,
- nickuserhostcpy, tokens[2], ircdstrings->ircnick);
- // Update our nick
- strcpy(ircdstrings->ircnick, prefixcopy);
- debugprint(DEBUG_FULL, "Updated ircnick to '%s'.\n", ircdstrings->ircnick);
- free(nickuserhostcpy);
- free(prefixcopy);
- }
-
- // Relay to all clients
- sendtoallclients(clients, str, sourcefd, settings);
-
- free(svrprefixcopy);
- free(strcopyPtr);
- return 1;
- }
-
- // Server MODE received? See what sort it is and act accordingly.
- if (strncmp(tokens[1], "MODE", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server MODE found and it is: %s with length %zd! Next token is '%s'. Analysing...\n",
- tokens[1], strlen(tokens[1]), tokens[2]);
-
- // 4 tokens could be either our initial mode or a channel mode (or something else - TODO - what else?)
- if (counter == 4) {
- // Might be our initial mode (e.g. ":nick MODE nick :+iwz")
- char comparison[MAXDATASIZE];
- snprintf(comparison, MAXDATASIZE, ":%s MODE %s :", ircdstrings->ircnick, ircdstrings->ircnick);
- if (strncmp(str, comparison, strlen(comparison)) == 0) {
- // Looks like it!
- debugprint(DEBUG_FULL, "Our initial MODE found (%s), storing for later.\n", tokens[3]);
- // Store in ircdstrings for when clients connect and relay to current clients.
- strcpy(ircdstrings->mode, tokens[3]);
-
- // Relay to all current clients anyway - TODO - Necessary?
- sendtoallclients(clients, str, sourcefd, settings);
-
- free(strcopyPtr);
- return 1;
- }
-
- // Might be a channel mode (e.g. ":nick!user@host MODE #channel +s")
- if (tokens[2][0] == '#') {
- // Looks like it! Tell all clients.
- debugprint(DEBUG_FULL, "Channel MODE found (%s %s), telling all clients.\n", tokens[2], tokens[3]);
- sendtoallclients(clients, str, sourcefd, settings);
-
- free(strcopyPtr);
- return 1;
- }
- }
-
- // Relay to all current clients if not processed by the above
- sendtoallclients(clients, str, sourcefd, settings);
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 324 (RPL_CHANNELMODEIS) received? Send to any clients who requested a channel MODE.
- if (strncmp(tokens[1], "324", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 324 (RPL_CHANNELMODEIS) found and it is: %s with length %zd! Sending to clients who are pending this.\n",
- tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingchannelmode == 1) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag
- clients[i].pendingchannelmode = 0;
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 368 (RPL_ENDOFBANLIST) received? Send to any clients who requested a ban MODE query. - TODO - Identify and handle start/middle of ban responses.
- if (strncmp(tokens[1], "368", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 368 (RPL_ENDOFBANLIST) found and it is: %s with length %zd! Sending to clients who are pending this.\n",
- tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingban == 1) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag
- clients[i].pendingban = 0;
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 329 (RPL_CREATIONTIME), 352 (RPL_WHOREPLY), or 315 (RPL_ENDOFWHO) received? Send to any clients who requested a WHO.
- if (strncmp(tokens[1], "329", strlen(tokens[1])) == 0 || strncmp(tokens[1], "352", strlen(tokens[1])) == 0 || strncmp(tokens[1], "315", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 329 (RPL_CREATIONTIME), 352 (RPL_WHOREPLY), or 315 (RPL_ENDOFWHO) found and it is: %s with length %zd! Sending to clients who are pending one of these.\n",
- tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingwho == 1 && clients[i].fd) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag if it's 315 (RPL_ENDOFWHO)
- if (strncmp(tokens[1], "315", strlen(tokens[1])) == 0) {
- clients[i].pendingwho = 0;
- }
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 321 (RPL_LISTSTART), 322 (RPL_LIST), or 323 (RPL_LISTEND) received? Send to any clients who requested a WHO.
- if (strncmp(tokens[1], "321", strlen(tokens[1])) == 0 || strncmp(tokens[1], "322", strlen(tokens[1])) == 0 || strncmp(tokens[1], "323", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 321 (RPL_LISTSTART), 322 (RPL_LIST), or 323 (RPL_LISTEND) found and it is: %s with length %zd! Sending to clients who are pending one of these.\n",
- tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendinglist == 1 && clients[i].fd) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag if it's 323 (RPL_LISTEND)
- if (strncmp(tokens[1], "323", strlen(tokens[1])) == 0) {
- clients[i].pendinglist = 0;
- }
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 307 (RPL_SUSERHOST), 311 (RPL_WHOISUSER), 312 (RPL_WHOISSERVER), 313 (RPL_WHOISOPERATOR), 317 (RPL_WHOISIDLE),
- // 319 (RPL_WHOISCHANNELS), 320 (RPL_WHOISSPECIAL), 671 (RPL_WHOISSECURE), or 318 (RPL_ENDOFWHOIS) received?
- // Send to any clients who requested a WHOIS.
- if (strncmp(tokens[1], "307", strlen(tokens[1])) == 0 || strncmp(tokens[1], "311", strlen(tokens[1])) == 0 ||
- strncmp(tokens[1], "312", strlen(tokens[1])) == 0 || strncmp(tokens[1], "313", strlen(tokens[1])) == 0 ||
- strncmp(tokens[1], "317", strlen(tokens[1])) == 0 || strncmp(tokens[1], "319", strlen(tokens[1])) == 0 ||
- strncmp(tokens[1], "320", strlen(tokens[1])) == 0 || strncmp(tokens[1], "671", strlen(tokens[1])) == 0 ||
- strncmp(tokens[1], "318", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 307 RPL_SUSERHOST, 311 RPL_WHOISUSER, 312 RPL_WHOISSERVER, 313 (RPL_WHOISOPERATOR), 317 RPL_WHOISIDLE, "
- "319 RPL_WHOISCHANNELS, 320 (RPL_WHOISSPECIAL), 671 (RPL_WHOISSECURE), or 318 RPL_ENDOFWHOIS found and it is: "
- "%s with length %zd! Sending to clients who are pending one of these.\n", tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingwhois == 1 && clients[i].fd) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag if it's 318 RPL_ENDOFWHOIS
- if (strncmp(tokens[1], "318", strlen(tokens[1])) == 0) {
- clients[i].pendingwhois = 0;
- }
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 314 (RPL_WHOWASUSER), 406 (ERR_WASNOSUCHNICK), or 369 (RPL_ENDOFWHOWAS) received?
- // Send to any clients who requested a WHOWAS.
- if (strncmp(tokens[1], "314", strlen(tokens[1])) == 0 || strncmp(tokens[1], "406", strlen(tokens[1])) == 0 || strncmp(tokens[1], "369", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "314 (RPL_WHOWASUSER), 406 (ERR_WASNOSUCHNICK), or 369 (RPL_ENDOFWHOWAS) "
- "found and it is: %s with length %zd! Sending to clients who are pending one of these.\n", tokens[1], strlen(tokens[1]));
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingwhowas == 1 && clients[i].fd) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // And clear the pending flag if it's 369 RPL_ENDOFWHOWAS
- if (strncmp(tokens[1], "369", strlen(tokens[1])) == 0) {
- clients[i].pendingwhowas = 0;
- }
- }
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 312 (RPL_WHOISSERVER) received? Check to see if anyone was pending a WHOIS or a WHOWAS and send to them, if not send to everyone.
- if (strncmp(tokens[1], "312", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 312 (RPL_WHOISSERVER) found and it is: %s with length %zd! Sending to clients who are pending this or to everyone if nobody is.\n",
- tokens[1], strlen(tokens[1]));
-
- int waspending = 0;
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingwhois == 1 || clients[i].pendingwhowas == 1) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // Note that we were pending this
- waspending = 1;
- }
- }
-
- // If no client was pending this, send to everyone
- if (!waspending) {
- sendtoallclients(clients, str, 0, settings);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
-
- // Server 401 (ERR_NOSUCHNICK) received? Check to see if anyone was pending a WHOIS and send to them,
- // if not send to everyone (401 was probably in reply to something else like a PRIVMSG).
- if (strncmp(tokens[1], "401", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server 401 (ERR_NOSUCHNICK) found and it is: %s with length %zd! Sending to clients who are pending this or to everyone if nobody is.\n",
- tokens[1], strlen(tokens[1]));
-
- int waspending = 0;
-
- // Relay to all pending clients
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].pendingwhois == 1) {
- sendtoclient(clients[i].fd, str, clients, settings, 0);
- // Note that we were pending this
- waspending = 1;
- }
- }
-
- // If no client was pending this, send to everyone
- if (!waspending) {
- sendtoallclients(clients, str, 0, settings);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server 432 (ERR_ERRONEUSNICKNAME) or 433 (ERR_NICKNAMEINUSE) received? See which nick we're on and try another.
- // (But only if we're not already registered with the real IRC server.)
- if ((strncmp(tokens[1], "432", strlen(tokens[1])) == 0 || strncmp(tokens[1], "433", strlen(tokens[1])) == 0) && !strlen(ircdstrings->greeting004)) {
- debugprint(DEBUG_SOME, "Server 432 (ERR_ERRONEUSNICKNAME) or 433 (ERR_NICKNAMEINUSE) found and it is: %s with length %zd! Trying another nick...\n",
- tokens[1], strlen(tokens[1]));
-
- // Try to get nick2 and nick3 from the configuration file
- char nick2[MAXNICKLENGTH];
- char nick3[MAXNICKLENGTH];
- if (!getconfstr("nick2", settings->conffile, nick2)) {
- nick2[0] = '\0';
- }
- if (!getconfstr("nick3", settings->conffile, nick3)) {
- nick3[0] = '\0';
- }
-
- // Do we have both a nick2 and a nick3? (And not tried autonick yet.)
- if (nick2[0] && nick3[0] && !ircdstrings->autonicknum) {
- // Has nick3 already been tried?
- if (strncmp(ircdstrings->ircnick, nick3, strlen(settings->ircnick)) == 0) {
- // Then try autonick
- tryautonick(ircdstrings);
- // Has nick2 already been tried?
- } else if (strncmp(ircdstrings->ircnick, nick2, strlen(settings->ircnick)) == 0) {
- // Then try nick3
- debugprint(DEBUG_SOME, "Trying nick3, nick2 was already tried.\n");
- strcpy(ircdstrings->ircnick, nick3);
- // Have neither been tried?
- } else {
- // Then try nick2
- debugprint(DEBUG_SOME, "Trying nick2, nick3 is also configured.\n");
- strcpy(ircdstrings->ircnick, nick2);
- }
- // Do we only have a nick2? (And not tried autonick yet.)
- } else if (nick2[0] && !ircdstrings->autonicknum) {
- // Has it already been tried?
- if (strncmp(ircdstrings->ircnick, nick2, strlen(settings->ircnick)) == 0) {
- // Then try autonick
- tryautonick(ircdstrings);
- } else {
- // Then try it
- debugprint(DEBUG_SOME, "Trying nick2, nick3 is not configured.\n");
- strcpy(ircdstrings->ircnick, nick2);
- }
- // Do we have neither? (Or have already started autonick.)
- } else {
- // Then try autonick
- tryautonick(ircdstrings);
- }
-
- // Set whichever one we settled on in the settings in case we reference settings later
- strcpy(settings->ircnick, ircdstrings->ircnick);
-
- // Try it with the server
- char outgoingmsg[MAXDATASIZE];
- snprintf(outgoingmsg, MAXDATASIZE, "NICK %s", ircdstrings->ircnick);
- // sourcefd = 0 as this is a trusted message
- sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
-
- free(strcopyPtr);
- return 1;
- }
-
- // Server CAP received?
- if (strncmp(tokens[1], "CAP", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server CAP found and it is: %s with length %zd! Analysing...\n", tokens[1], strlen(tokens[1]));
- // If the server said "CAP <ournick> ACK :multi-prefix" then it must have approved our CAP multi-prefix request
- if (counter == 5) {
- if (strncmp(tokens[2], ircdstrings->ircnick, strlen(tokens[2])) == 0 &&
- strncmp(tokens[3], "ACK", strlen(tokens[3])) == 0 &&
- strncmp(tokens[4], ":multi-prefix", strlen(tokens[4])) == 0) {
- ircdstrings->capmultiprefix = 1;
- }
- }
- // We didn't handle it
- debugprint(DEBUG_FULL, "Unhandled server CAP response.\n");
- }
-
- // Server NOTICE received? Handle and log if it's from a user, otherwise let pass through to default handler.
- if (strncmp(tokens[1], "NOTICE", strlen(tokens[1])) == 0) {
- debugprint(DEBUG_FULL, "Server NOTICE found and it is: %s with length %zd! Analysing...\n", tokens[1], strlen(tokens[1]));
- // If the first token is a nick!user@host then it's probably from a user
- if (strstr(tokens[0], "!") != NULL && strstr(tokens[0], "@") != NULL) {
- debugprint(DEBUG_FULL, "Server NOTICE appears to be from a user, sending to all clients and logging.\n");
-
- sendtoallclients(clients, str, 0, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(str, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(str, settings->ircnick, settings->basedir, LOG_PRIVMSG);
- }
-
- free(strcopyPtr);
- return 1;
- } else {
- debugprint(DEBUG_FULL, "Server NOTICE does not appear to be from a user, passing through.\n");
- }
- }
- }
-
- // Don't return if we got here because this means we didn't process something above
-
+ // Don't return if we got here because this means we didn't process something in processservermessage()
break;
case SOURCE_CLIENT: // If message(s) were from a real IRC client
- // PASS received? User is trying to log in, check their password.
- if (strncasecmp(tokens[0], "PASS", strlen(tokens[0])) == 0) {
- if (checkpassword(tokens[1], settings->conffile)) {
- debugprint(DEBUG_FULL, "Password accepted! Setting fd %d to authenticated.\n", sourcefd);
- // Find the client in the clients array and set them as authenticated
- for (int i = 0; i < MAXCLIENTS; i++) {
- if (clients[i].fd == sourcefd) {
- // Found client in array, set to authenticated
- clients[i].authed = 1;
- debugprint(DEBUG_FULL, "Found and authenticated fd in arr_authed.\n");
- // Alert other clients about the successful authentication
- char alertmsg[MAXDATASIZE];
- if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: new client with fd %d has successfully authenticated.", ircdstrings->ircnick, sourcefd)) {
- fprintf(stderr, "Error while preparing authentication success NOTICE!\n");
- debugprint(DEBUG_CRIT, "Error while preparing authentication success NOTICE!\n");
- alertmsg[0] = '\0';
- }
- // "except" the current fd - we can use this as "except/sourcefd" since we set them as authed just above
- sendtoallclients(clients, alertmsg, sourcefd, settings);
- }
- }
- } else {
- debugprint(DEBUG_SOME, "Password rejected, disconnecting fd %d.\n", sourcefd);
- disconnectclient(sourcefd, clients, ircdstrings, settings);
- // Alert other clients about the failed authentication
- char alertmsg[MAXDATASIZE];
- if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: new client with fd %d has failed to authenticate.", ircdstrings->ircnick, sourcefd)) {
- fprintf(stderr, "Error while preparing authentication failure NOTICE!\n");
- debugprint(DEBUG_CRIT, "Error while preparing authentication failure NOTICE!\n");
- alertmsg[0] = '\0';
- }
- // "except" 0 since we trust this message
- sendtoallclients(clients, alertmsg, 0, settings);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // CAP received? Clients can send CAP before PASS so we have to deal with this even if they are not authenticated yet.
- if (strncasecmp(tokens[0], "CAP", strlen(tokens[0])) == 0) {
- // But only do something if the real server told us it had a CAP (only multi-prefix for now)
- if (ircdstrings->capmultiprefix == 1) {
- debugprint(DEBUG_FULL, "Client CAP received and the server supports CAPs, continuing.\n");
- } else {
- debugprint(DEBUG_FULL, "Client CAP received but the server doesn't support CAPs, returning.\n");
- free(strcopyPtr);
- return 1;
- }
- // Get the real IRC server name from greeting001
- // They are now pending CAP negotiation
- clients[arrindex(clients, sourcefd)].pendingcap = 1;
- char outgoingmsg[MAXDATASIZE];
- // If client is requesting CAP list, send it...
- if (strncasecmp(tokens[1], "LS", strlen(tokens[1])) == 0) {
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s CAP * LS :multi-prefix", ircdstrings->ircdname)) {
- fprintf(stderr, "Error while preparing CAP LS response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing CAP LS response!\n");
- outgoingmsg[0] = '\0';
- }
- // ...even if unauthenticated
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 1);
- free(strcopyPtr);
- return 1;
- // If client is requesting a CAP...
- } else if (strncasecmp(tokens[1], "REQ", strlen(tokens[1])) == 0) {
- // ...and it is "multi-prefix", send it
- if (strncasecmp(tokens[2], ":multi-prefix", strlen(tokens[2])) == 0) {
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s CAP %s ACK :multi-prefix ", ircdstrings->ircdname, ircdstrings->ircnick)) {
- fprintf(stderr, "Error while preparing CAP ACK response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing CAP ACK response!\n");
- outgoingmsg[0] = '\0';
- }
- // ...even if unauthenticated
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 1);
- free(strcopyPtr);
- return 1;
- }
- // If client is finishing CAP negotiation then mark them as so
- } else if (strncasecmp(tokens[1], "END", strlen(tokens[1])) == 0) {
- clients[arrindex(clients, sourcefd)].pendingcap = -1;
- }
- }
-
- // We're past PASS in the list of possible commands, so ignore
- // anything else the client says if they are not authenticated yet.
- if (!clients[arrindex(clients, sourcefd)].authed) {
- debugprint(DEBUG_CRIT, "Ignoring client command '%s' from sourcefd '%d' as not authenticated yet.\n", tokens[0], sourcefd);
- free(strcopyPtr);
- return 1;
- }
-
- // USER received and not pending CAP negotiation during registration?
- // Or client has just finished negotiating CAP (pendingcap = -1)?
- // If so, assume this is a new client connecting and catch them on up on the state
- if ((strncasecmp(tokens[0], "USER", strlen(tokens[0])) == 0 && clients[arrindex(clients, sourcefd)].pendingcap == 0) || clients[arrindex(clients, sourcefd)].pendingcap == -1) {
- // Somewhere to store the several strings we will need to build and send
- char outgoingmsg[MAXDATASIZE]; // String to send to client
-
- // If registering then they must no longer be pending CAP negotiation
- clients[arrindex(clients, sourcefd)].pendingcap = 0;
-
- // Tell the client to go away if we aren't registered with the real server yet as defined by the last greeting not being set yet
- if (!strlen(ircdstrings->greeting004)) {
- sendtoclient(sourcefd, "Sorry, we aren't registered with a real IRC server yet.", clients, settings, 0);
- disconnectclient(sourcefd, clients, ircdstrings, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Send IRC greeting strings (001/RPL_WELCOME, 002/RPL_YOURHOST, 003/RPL_CREATED, 004/RPL_MYINFO, 005/RPL_ISUPPORT) to client
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting001);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting002);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting003);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- if (ircdstrings->greeting005a[0]) {
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005a);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
- if (ircdstrings->greeting005b[0]) {
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005b);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
- if (ircdstrings->greeting005c[0]) {
- snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005c);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- // Send our own greeting message
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Welcome to blabouncer!", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using \"/QUOTE BLABOUNCER\"", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- // Get the channel count so we can enumerate over all channels.
- // Storing separately so we can skip over blank channels.
- int channelcount = getchannelcount(channels);
- // Set the client as pending RPL_NAMREPLYs for 'channelcount' channels
- debugprint(DEBUG_FULL, "Setting pendingnames to '%d' for client with fd '%d'.\n", channelcount, sourcefd);
- clients[arrindex(clients, sourcefd)].pendingnames = channelcount;
-
- // Get client to join channels, and tell client about those channels
- for (int i = 0; i < channelcount; i++) {
- debugprint(DEBUG_FULL, "JOINing channel[%d] out of %d.\n", i, channelcount);
- // Skip this one and increment channelcount if it's a blank channel
- if (!channels[i].name[0]) {
- debugprint(DEBUG_FULL, "Skipping channel[%d], incrementing channelcount.\n", i);
- channelcount++;
- continue;
- }
-
- // Get client to join channels
- if (!snprintf(outgoingmsg, MAXDATASIZE, "%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) {
- fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n");
- debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses!\n");
- free(strcopyPtr);
- return 0;
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- // Send topic (or lack thereof) to client
- // If there isn't one set (we guess this if topic timestamp is 0), send 331 RPL_NOTOPIC
- if (strncmp(channels[i].topicwhen, "0", 1) == 0) {
- // Prepare the no topic message...
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 331 %s %s :No topic is set.", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name)) {
- fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 331 RPL_NOTOPIC!\n");
- debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 331 RPL_NOTOPIC!\n");
- free(strcopyPtr);
- return 0;
- }
- // ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- // If there is one set, send 332 RPL_TOPIC and 333 RPL_TOPICWHOTIME
- } else {
- // Prepare the topic message...
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 332 %s %s :%s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topic)) {
- fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 332 RPL_TOPIC!\n");
- debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 332 RPL_TOPIC!\n");
- free(strcopyPtr);
- return 0;
- }
- // ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- // Next prepare the topic who/when message...
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 333 %s %s %s %s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topicwho, channels[i].topicwhen)) {
- fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 333 RPL_TOPICWHOTIME!\n");
- debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 333 RPL_TOPICWHOTIME!\n");
- free(strcopyPtr);
- return 0;
- }
- // ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- // Get the latest RPL_NAMREPLY for this channel to relay to the client when it arrives
- char namesreq[MAXDATASIZE];
- snprintf(namesreq, MAXDATASIZE, "NAMES %s", channels[i].name);
- sendtoserver(server_ssl, namesreq, strlen(namesreq), 0, clients, settings);
- }
-
- // Send our mode to the client (if we have one)
- if (strlen(ircdstrings->mode) > 0) {
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s MODE %s %s", ircdstrings->ircnick, ircdstrings->ircnick, ircdstrings->mode)) {
- fprintf(stderr, "Error while preparing USER just connected, MODE response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing USER just connected, MODE response!\n");
- free(strcopyPtr);
- return 0;
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- // Set the client as registered
- clients[arrindex(clients, sourcefd)].registered = 1;
-
- // Catch the client up with the default number of seconds of replay
- if (!doreplay(sourcefd, settings->replayseconds, clients, settings, ircdstrings, channels)) {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Pretty much ignore anything else the client says if it's not registered yet,
- // as the first thing we want to hear is either PASS or USER
- if (!clients[arrindex(clients, sourcefd)].registered) {
- debugprint(DEBUG_SOME, "Ignoring client command '%s' from sourcefd '%d' as not registered yet.\n", tokens[0], sourcefd);
- free(strcopyPtr);
- return 1;
- }
-
- // Client PING received? If so, send a PONG back with the next element as the argument.
- if (strncasecmp(tokens[0], "PING", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0]));
-
- char outgoingmsg[MAXDATASIZE]; // String to send to client
- if (!snprintf(outgoingmsg, MAXDATASIZE, "PONG %s", tokens[1])) { // TODO - Make sure tokens[1] actually has a token
- fprintf(stderr, "Error while preparing PONG response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing PONG response!\n");
- outgoingmsg[0] = '\0';
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
+ if (processclientmessage(server_ssl, str, clients, sourcefd, ircdstrings, channels, settings, tokens, counter)) {
// We processed something so return true
free(strcopyPtr);
return 1;
}
- // TODO - Ignoring CAP for now so as not to confuse other clients, but we should probably query the server then relay the response to the client
- if (strncasecmp(tokens[0], "CAP", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client CAP found and it is: %s with length %zd! Ignoring completely for now - TODO - do something useful!\n", tokens[0], strlen(tokens[0]));
- free(strcopyPtr);
- return 1;
- }
-
- // Just send NICK to server and let it change all clients' nicks if needed
- if (strncasecmp(tokens[0], "NICK", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client NICK found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // If PRIVMSG received, send to server, but also reformat and send to all other clients and log to replay file.
- if (strncasecmp(tokens[0], "PRIVMSG", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client PRIVMSG found and it is: %s with length %zd! Sending to server then back to other clients...\n", tokens[0], strlen(tokens[0]));
- // Send original request straight to server
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
-
- // If it seems to be a CTCP VERSION request, just send to the server (CTCP requests are delimited with \1)
- if (counter == 3 && strncmp(tokens[2], ":\1VERSION\1", strlen(tokens[2])) == 0) {
- debugprint(DEBUG_FULL, "Client PRIVMSG looked like a CTCP VERSION request, so not sending to other clients.\n");
- free(strcopyPtr);
- return 1;
- }
-
- // Rebuild to full PRIVMSG string and relay to all other clients
- char outgoingmsg[MAXDATASIZE]; // String to send to client
-
- if (!snprintf(outgoingmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) {
- fprintf(stderr, "Error while preparing PRIVMSG relay from another bouncer client.\n");
- debugprint(DEBUG_CRIT, "Error while preparing PRIVMSG relay from another bouncer client.\n");
- free(strcopyPtr);
- return 0;
- }
- // Send to all except source client
- sendtoallclients(clients, outgoingmsg, sourcefd, settings);
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(outgoingmsg, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(outgoingmsg, settings->ircnick, settings->basedir, LOG_PRIVMSG);
- }
-
- free(strcopyPtr);
- return 1;
- }
-
- // Just send JOIN to server and let it talk back to clients as required
- if (strncasecmp(tokens[0], "JOIN", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client JOIN found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Don't do anything with QUIT, just let the client go - TODO: Let another clients know with a NOTICE or something
- // A client has QUIT, so disconnect (close) them and don't do anything else for now - TODO: Let another clients know with a NOTICE or something
- if (strncasecmp(tokens[0], "QUIT", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client QUIT found from fd %d and it is: %s with length %zd! Disconnecting that fd.\n", sourcefd, tokens[0], strlen(tokens[0]));
- disconnectclient(sourcefd, clients, ircdstrings, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Just send PART to server and let it talk back to clients as required
- if (strncasecmp(tokens[0], "PART", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client PART found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Just send TOPIC to server and let it talk back to clients as required
- if (strncasecmp(tokens[0], "TOPIC", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client TOPIC found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Take note of what sort of MODE was requested, mark the client as waiting for the reply (so not all clients get it)
- // Send it to the server either way and let it talk back to the requesting client as required
- if (strncasecmp(tokens[0], "MODE", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client MODE found and it is: %s with length %zd! Analysing...\n", tokens[0], strlen(tokens[0]));
- // Is it a ban MODE request (MODE #channel b)?
- if (counter >= 3 && strncmp(tokens[2], "b", strlen("b")) == 0) {
- debugprint(DEBUG_FULL, "Ban MODE request received, marking as pending.\n");
- clients[arrindex(clients, sourcefd)].pendingban = 1;
- } else if (counter == 2) {
- // Assume a normal channel mode request (MODE #channel) - TODO - What if it isn't!?
- debugprint(DEBUG_FULL, "Assuming channel MODE request received, marking as pending.\n");
- clients[arrindex(clients, sourcefd)].pendingchannelmode = 1;
- }
-
- // Either way, send it on the server
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // WHO requested, mark the client as waiting for the reply (so not all clients get it) and send it on the server
- if (strncasecmp(tokens[0], "WHO", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client WHO found and it is: %s with length %zd! Marking as pending.\n", tokens[0], strlen(tokens[0]));
- clients[arrindex(clients, sourcefd)].pendingwho = 1;
-
- // Either way, send it on the server
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // LIST requested, mark the client as waiting for the reply (so not all clients get it) and send it on the server
- if (strncasecmp(tokens[0], "LIST", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client LIST found and it is: %s with length %zd! Marking as pending.\n", tokens[0], strlen(tokens[0]));
- clients[arrindex(clients, sourcefd)].pendinglist = 1;
-
- // Either way, send it on the server
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Client WHOIS received, send straight on to server and mark the client as pending the response
- if (strncasecmp(tokens[0], "WHOIS", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client WHOIS found and it is: %s with length %zd! Sending to server and setting client as pending.\n", tokens[0], strlen(tokens[0]));
- clients[arrindex(clients, sourcefd)].pendingwhois = 1;
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Client WHOWAS received, send straight on to server and mark the client as pending the response
- if (strncasecmp(tokens[0], "WHOWAS", strlen(tokens[0])) == 0) {
- debugprint(DEBUG_FULL, "Client WHOWAS found and it is: %s with length %zd! Sending to server and setting client as pending.\n", tokens[0], strlen(tokens[0]));
- clients[arrindex(clients, sourcefd)].pendingwhowas = 1;
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // Client NOTICE received
- if (strncasecmp(tokens[0], "NOTICE", strlen(tokens[0])) == 0) {
-
- // Rebuild to full NOTICE string including our nick!user@host for logging
- char fullmsg[MAXDATASIZE];
-
- if (!snprintf(fullmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) {
- fprintf(stderr, "Error while preparing NOTICE string for logging.\n");
- debugprint(DEBUG_CRIT, "Error while preparing NOTICE string for logging.\n");
- free(strcopyPtr);
- return 0;
- }
-
- // Write to replay log if replay logging enabled
- if (settings->replaylogging) {
- writereplayline(fullmsg, settings->basedir);
- }
-
- // Write to normal log if logging enabled
- if (settings->logging) {
- logline(fullmsg, settings->ircnick, settings->basedir, LOG_PRIVMSG);
- }
-
- // If it's a CTCP VERSION response then only send to the server (CTCP requests are delimited with \1)
- if (counter >= 3 && strncmp(tokens[2], ":\1VERSION", strlen(tokens[2])) == 0) {
- debugprint(DEBUG_FULL, "Client NOTICE looked like a CTCP VERSION response, so just sending to the server.\n");
- sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
- free(strcopyPtr);
- return 1;
- }
-
- // If it wasn't a CTCP VERSION response, then let this fall through to the default unhandled action by not returning here
- }
-
- // Custom BLABOUNCER command received
- // Case insensitive comparisons here since clients won't be recognising and uppercasing these commands
- if (strncasecmp(tokens[0], "BLABOUNCER", strlen(tokens[0])) == 0) {
- char outgoingmsg[MAXDATASIZE];
- debugprint(DEBUG_FULL, "Client BLABOUNCER found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
- // REPLAY received, send the requested length of replay time to the client
- if (strncasecmp(tokens[1], "REPLAY", strlen("REPLAY")) == 0) {
- debugprint(DEBUG_FULL, "Client BLABOUNCER REPLAY (custom blabouncer command) found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
-
- // Split the request into days:hours:minutes:seconds
-
- // Track which colon-separated token within this request we're on
- int timecounter = 0;
-
- // Build array of colon-separated tokens
- char timetokens[MAXTOKENS][MAXDATASIZE];
- // Copy to a temporary string so we still have the original in case it's not processed
- char *timestrcopy = strdup(tokens[2]);
- // Keep track of initial pointer for free()ing later
- char *timestrcopyPtr = timestrcopy;
-
- char *timetoken;
- while ((timetoken = strsep(&timestrcopy, ":")) != NULL) {
- if (*timetoken == '\0') continue; // Skip consecutive matches
- if (timecounter >= MAXTOKENS) break; // Too many tokens
- debugprint(DEBUG_FULL, "Time token: \"%s\", length %zd.\n", timetoken, strlen(timetoken));
- // Copy into the token array (strlen + 1 to get the NULL terminator)
- strncpy(timetokens[timecounter], timetoken, strlen(timetoken) + 1);
- timecounter++;
- }
-
- // Make sure we don't have more than four (d:h:m:s) components
- if (timecounter > 4) {
- debugprint(DEBUG_SOME, "Too many time components requested by REPLAY command. Telling client.\n");
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Too many time components requestd by REPLAY command. Expected up to four (days:hours:minutes:seconds).", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- free(timestrcopyPtr);
- free(strcopyPtr);
- return 1;
- }
-
- // Make sure all the components are numbers
- for (int i = 0; i < timecounter; i++) {
- // Temporary number and pointer for checking errors
- long check;
- char *str_end;
- errno = 0;
- check = strtol(timetokens[i], &str_end, 10);
- if (str_end == timetokens[i] || ((check == LONG_MAX || check == LONG_MIN) && errno == ERANGE)) {
- debugprint(DEBUG_SOME, "Invalid number '%s' requested by REPLAY command. Telling client.\n", timetokens[i]);
- if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Invalid number '%s' requested by REPLAY command.", ircdstrings->ircnick, timetokens[i])) {
- fprintf(stderr, "Error while preparing REPLAY invalid number response!\n");
- debugprint(DEBUG_CRIT, "Error while preparing REPLAY invalid number response!\n");
- outgoingmsg[0] = '\0';
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- free(timestrcopyPtr);
- free(strcopyPtr);
- return 1;
- }
- }
-
- // How many seconds we're going to replay
- int replayseconds = 0;
-
- // If d:h:m:s provided
- if (timecounter == 4) {
- replayseconds += 86400 * strtol(timetokens[0], NULL, 10);
- replayseconds += 3600 * strtol(timetokens[1], NULL, 10);
- replayseconds += 60 * strtol(timetokens[2], NULL, 10);
- replayseconds += strtol(timetokens[3], NULL, 10);
- }
- // If h:m:s provided
- if (timecounter == 3) {
- replayseconds += 3600 * strtol(timetokens[0], NULL, 10);
- replayseconds += 60 * strtol(timetokens[1], NULL, 10);
- replayseconds += strtol(timetokens[2], NULL, 10);
- }
- // If m:s provided
- if (timecounter == 2) {
- replayseconds += 60 * strtol(timetokens[0], NULL, 10);
- replayseconds += strtol(timetokens[1], NULL, 10);
- }
- // If s provided
- if (timecounter == 1) {
- replayseconds += strtol(timetokens[0], NULL, 10);
- }
-
- debugprint(DEBUG_FULL, "Replaying '%s' which is '%d' seconds.\n", tokens[2], replayseconds);
-
- if (!doreplay(sourcefd, replayseconds, clients, settings, ircdstrings, channels)) {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
- free(timestrcopyPtr);
- free(strcopyPtr);
- return 1;
- // QUIT received, send QUIT message to server and exit cleanly
- } else if (strncasecmp(tokens[1], "QUIT", strlen("QUIT")) == 0) {
- debugprint(DEBUG_SOME, "Client BLABOUNCER QUIT found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
- // Combine "QUIT :" with any optional quit message the user provided
- if (counter > 2) {
- // Any optional quit message comes 16 characters after the start of the string ("BLABOUNCER QUIT " is 16 characters)
- cleanexit(server_ssl, clients, sourcefd, ircdstrings, settings, str + 16);
- } else {
- cleanexit(server_ssl, clients, sourcefd, ircdstrings, settings, "");
- }
-
- // Unrecognised BLABOUNCER command received, send some help instructions
- } else {
- debugprint(DEBUG_SOME, "Client BLABOUNCER unrecognised command found and it is: %s with length %zd! Sending a help message.\n", tokens[1], strlen(tokens[1]));
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unrecognised BLABOUNCER command received. Valid commands are:", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstrings->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- free(strcopyPtr);
- return 1;
- }
- }
-
+ // Don't return if we got here because this means we didn't process something in processclientmessage()
break;
default:
fprintf(stderr, "Unexpected raw IRC string source!\n");
@@ -1526,7 +225,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
// =============================================>
- debugprint(DEBUG_FULL, "Done with processircmessage()\n");
+ debugprint(DEBUG_FULL, "Done with processircmessage(), didn't process anything.\n");
// If we got here then we didn't process anything
free(strcopyPtr);
diff --git a/functions.c b/functions.c
index 0aefa05..4ea5a51 100644
--- a/functions.c
+++ b/functions.c
@@ -912,3 +912,27 @@ void tryautonick(struct ircdstrings *ircdstrings) {
debugprint(DEBUG_FULL, "tryautonick(): set irdstrings->ircnick to '%s'.\n", ircdstrings->ircnick);
}
+
+// Exit the program cleanly - tell clients, tell the server, then exit(0)
+// Optional quit message string "quitmsg"
+// "sourcefd" of 0 means the request didn't come from a client
+void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings, struct settings *settings, char *quitmsg) {
+ char outgoingmsg[MAXDATASIZE];
+
+ // Tell clients and debug log
+ if (sourcefd) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Exiting on request from client with fd '%d', message '%s'.", ircdstrings->ircnick, sourcefd, quitmsg);
+ debugprint(DEBUG_CRIT, "Exiting on request from client with fd '%d', message '%s'.\n", sourcefd, quitmsg);
+ } else {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Exiting on request (not from a client), message '%s'.", ircdstrings->ircnick, quitmsg);
+ debugprint(DEBUG_CRIT, "Exiting on request (not from a client), message '%s'.\n", quitmsg);
+ }
+ sendtoallclients(clients, outgoingmsg, EXCEPT_NONE, settings);
+
+ // Tell the server
+ // Combine "QUIT :" with any (optional) quit message
+ snprintf(outgoingmsg, MAXDATASIZE, "QUIT :%s", quitmsg);
+ sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), sourcefd, clients, settings);
+
+ exit(0);
+}
diff --git a/functions.h b/functions.h
index cba1d72..91ce35f 100644
--- a/functions.h
+++ b/functions.h
@@ -36,11 +36,6 @@
#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
#define NO_INPUT 1
@@ -50,6 +45,13 @@
#define DEBUG_SOME 1
#define DEBUG_FULL 2
+#define EXCEPT_NONE 0
+
+#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
+
// Write debug string to file.
// Debug level is provided by level, set to one of DEBUG_CRIT, DEBUG_SOME or DEBUG_FULL.
// Debug is only written if the global int "debug" is greater than or equal to the level.
@@ -144,4 +146,9 @@ int joinautochannels(SSL *server_ssl, struct client *clients, struct settings *s
// 1 through to 9.
void tryautonick(struct ircdstrings *ircdstrings);
+// Exit the program cleanly - tell clients, tell the server, then exit(0)
+// Optional quit message string "quitmsg"
+// "sourcefd" of 0 means the request didn't come from a client
+void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings, struct settings *settings, char *quitmsg);
+
#endif
diff --git a/message.c b/message.c
new file mode 100644
index 0000000..f837b83
--- /dev/null
+++ b/message.c
@@ -0,0 +1,1264 @@
+/*
+ * This file is part of blabouncer (https://www.blatech.co.uk/l_bratch/blabouncer).
+ * Copyright (C) 2019 Luke Bratch <luke@bratch.co.uk>.
+ *
+ * Blabouncer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * Blabouncer is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with blabouncer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "message.h"
+
+// Process an IRC message that came from the real server.
+// Return 1 if we processed it, or 0 if we didn't.
+int processservermessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings,
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter) {
+ // Record that we received something from the server for timeout checking purposes
+ ircdstrings->lastmessagetime = time(NULL); // snprintf(NULL, 0, "%ld", timenow);
+
+ // Server PING received? If so, send a PONG back with the next element as the argument.
+ if (strncmp(tokens[0], "PING", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Server PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0]));
+
+ char outgoingmsg[MAXDATASIZE]; // String to send to server
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "PONG %s", tokens[1])) { // TODO - Make sure tokens[1] actually has a token
+ fprintf(stderr, "Error while preparing PONG response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing PONG response!\n");
+ outgoingmsg[0] = '\0';
+ }
+ // sourcefd = 0 as this is a trusted response
+ sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
+
+ // We processed something so return true
+ return 1;
+ }
+
+ // Prefix received? TODO - Care about what the prefix is - what if it's a different server/network/whatever?
+ if (tokens[0][0] == ':') {
+ debugprint(DEBUG_FULL, "Prefix found: '%s'! Next token is '%s', length %zd.\n", tokens[0], tokens[1], strlen(tokens[1]));
+ // Greetings 001 through to 005, store in ircdstrings array for resending when clients connect
+ // Also store our nick!user@host from greeting 001
+ // Also store the real IRCd's name from greeting 004
+ if (strncmp(tokens[1], "001", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Found greeting 001 (%s) (length %zd), storing in ircdstrings struct.\n", str, strlen(str));
+ strncpy(ircdstrings->greeting001, str, strlen(str));
+ // Null the end of the string
+ ircdstrings->greeting001[strlen(str)] = '\0';
+ debugprint(DEBUG_FULL, "Storing our nick!user@host (:%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]);
+ // Prepend a colon (:) first since everything (so far) needs one
+ if (!snprintf(ircdstrings->nickuserhost, MAXDATASIZE, ":%s", tokens[counter - 1])) {
+ fprintf(stderr, "Error while preparing nickuserhost for storage!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing nickuserhost for storage!\n");
+ exit(1);
+ }
+ // Null the end of the new string
+ ircdstrings->nickuserhost[strlen(tokens[counter - 1]) + 1] = '\0'; // +1 for the inserted colon
+ debugprint(DEBUG_FULL, "nickuserhost '%s' stored.\n", ircdstrings->nickuserhost);
+ return 1;
+ } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Found greeting 002 (%s), storing in ircdstrings struct.\n", str);
+ strncpy(ircdstrings->greeting002, str, strlen(str));
+ // Null the end of the string
+ ircdstrings->greeting002[strlen(str)] = '\0';
+ return 1;
+ } else if (strncmp(tokens[1], "003", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Found greeting 003 (%s), storing in ircdstrings struct.\n", str);
+ strncpy(ircdstrings->greeting003, str, strlen(str));
+ // Null the end of the string
+ ircdstrings->greeting003[strlen(str)] = '\0';
+ return 1;
+ } else if (strncmp(tokens[1], "004", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Found greeting 004 (%s), storing in ircdstrings struct.\n", str);
+ strncpy(ircdstrings->greeting004, str, strlen(str));
+ // Null the end of the string
+ ircdstrings->greeting004[strlen(str)] = '\0';
+ debugprint(DEBUG_FULL, "Storing the real IRCd's name (%s) from greeting 004 in ircdstrings struct.\n", tokens[3]);
+ strncpy(ircdstrings->ircdname, tokens[3], strlen(tokens[3]));
+ // Null the end of the string
+ ircdstrings->ircdname[strlen(tokens[3])] = '\0';
+ // Receiving greeting 004 means we're now registered
+ // Request IRCv3 multi-prefix extension so we can more accurately inform new clients about current user prefixes
+ sendtoserver(server_ssl, "CAP REQ multi-prefix", strlen("CAP REQ multi-prefix"), 0, clients, settings);
+ // Send the connect command, if set
+ if (settings->connectcommand[0]) {
+ sendtoserver(server_ssl, settings->connectcommand, strlen(settings->connectcommand), 0, clients, settings);
+ }
+ // If this is a reconnection, JOIN existing channels and catch clients up again
+ if (ircdstrings->reconnecting) {
+ // First tell clients if our nick changed
+ if (!strcmp(ircdstrings->ircnick, ircdstrings->oldnick) == 0) {
+ debugprint(DEBUG_SOME, "Telling clients about nick change.\n");
+ char nickmsg[MAXDATASIZE];
+ snprintf(nickmsg, MAXDATASIZE, ":%s NICK :%s", ircdstrings->oldnick, ircdstrings->ircnick);
+ sendtoallclients(clients, nickmsg, sourcefd, settings);
+ }
+
+ // Next re-join channels
+ // Storing separately so we can skip over blank channels.
+ int channelcount = getchannelcount(channels);
+ // Join all the channels and make a note of the channel count
+ for (int i = 0; i < channelcount; i++) {
+ // Skip this one and increment channelcount if it's a blank channel
+ if (!channels[i].name[0]) {
+ debugprint(DEBUG_FULL, "Reconnection: Skipping channel[%d], incrementing channelcount.\n", i);
+ channelcount++;
+ continue;
+ }
+
+ debugprint(DEBUG_SOME, "Reconnection: Re-joining '%s'.\n", channels[i].name);
+
+ char joinmsg[MAXDATASIZE];
+ snprintf(joinmsg, MAXDATASIZE, "JOIN %s", channels[i].name);
+ sendtoserver(server_ssl, joinmsg, strlen(joinmsg), 0, clients, settings);
+ }
+
+ // Finally do a replay for all clients and tell them we're reconnected
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd) {
+ char alertmsg[MAXDATASIZE];
+ if (!doreplay(clients[i].fd, settings->replayseconds, clients, settings, ircdstrings, channels)) {
+ snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
+ sendtoclient(sourcefd, alertmsg, clients, settings, 0);
+ }
+ snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Reconnection complete.", ircdstrings->ircnick);
+ sendtoclient(clients[i].fd, alertmsg, clients, settings, 0);
+ }
+ }
+
+ // Reconnection complete
+ ircdstrings->oldnick[0] = '\0';
+ ircdstrings->reconnecting = 0;
+ // If it's not, deal with auto channels
+ } else {
+ // Join any auto channels set in the configuration file
+ joinautochannels(server_ssl, clients, settings);
+ }
+ return 1;
+ } else if (strncmp(tokens[1], "005", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Found greeting 005 (%s), storing in ircdstrings struct.\n", str);
+ // Find an empty greeting005 string in ircdstrings and store in there...
+ if (!ircdstrings->greeting005a[0]) {
+ strncpy(ircdstrings->greeting005a, str, strlen(str));
+ ircdstrings->greeting005a[strlen(str)] = '\0';
+ } else if (!ircdstrings->greeting005b[0]) {
+ strncpy(ircdstrings->greeting005b, str, strlen(str));
+ ircdstrings->greeting005b[strlen(str)] = '\0';
+ } else if (!ircdstrings->greeting005c[0]) {
+ strncpy(ircdstrings->greeting005c, str, strlen(str));
+ ircdstrings->greeting005c[strlen(str)] = '\0';
+ } else {
+ // ...or if they are all fill, discard - TODO - Support more than three greeting005 strings!
+ debugprint(DEBUG_CRIT, "Already stored three greeting 005 strings, discarding this one.\n");
+ }
+ return 1;
+ }
+
+ // Server JOIN received? Add to our local channel array if it's us, or record the user in the channel if it's not us.
+ if (strncmp(tokens[1], "JOIN", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server JOIN found and it is: %s with length %zd! Next token is '%s'. Adding to local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]);
+ // Next token should be the channel name but it probably needs the leading ':' stripping
+ debugprint(DEBUG_FULL, "processircmessage(): Channel name was '%s'\n", tokens[2]);
+ stripprefix(tokens[2]);
+ debugprint(DEBUG_FULL, "processircmessage(): Channel name now '%s'\n", tokens[2]);
+
+ // If the user JOINing is us, then we must have joined a channel, so add to our local channel array.
+ // Copy to a temporary string so we still have the original in case we need it
+ char *prefixcopy = strdup(tokens[0]);
+ // Just get the nick for comparison
+ extractnickfromprefix(prefixcopy);
+ if (strncmp(prefixcopy, ircdstrings->ircnick, strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Server JOIN: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
+ // TODO - Saner way to initialise this since we don't have the variables yet?
+ // TODO - Defaulting to type '=' which is "public" since I don't know what else to guess.
+ createchannel(channels, tokens[2], "TOPIC", "TOPICWHO", "0");
+ } else {
+ debugprint(DEBUG_FULL, "Server JOIN: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
+ }
+
+ // And then send to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(str, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(str, settings->ircnick, settings->basedir, LOG_JOINPART);
+ }
+
+ free(prefixcopy);
+ return 1;
+ }
+
+ // Server PART received? Remove from our local channel list if it's us.
+ if (strncmp(tokens[1], "PART", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server PART found and it is: %s with length %zd! Next token is '%s'. Removing from local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]);
+
+ // If the user PARTing is us, then we must have left a channel, so remove it from our local channel array.
+ // (If it's not us, then it's another user PARTing a channel, so just pass straight through to letting all our clients know.)
+ // Copy to a temporary string so we still have the original in case we need it
+ char *prefixcopy = strdup(tokens[0]);
+ // Just get the nick for comparison
+ extractnickfromprefix(prefixcopy);
+ if (strncmp(prefixcopy, ircdstrings->ircnick, strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Server PART: nick is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
+ removechannel(channels, tokens[2]);
+ } else {
+ debugprint(DEBUG_FULL, "Server PART: nick is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->ircnick);
+ }
+
+ // And then send to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(str, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(str, settings->ircnick, settings->basedir, LOG_JOINPART);
+ }
+
+ free(prefixcopy);
+ return 1;
+ }
+
+ // Channel topics/names/nicks/etc.
+ // Server 331 (RPL_NOTOPIC) the topic is blank which we track by having a set timestamp of 0
+ if (strncmp(tokens[1], "331", strlen(tokens[1])) == 0) {
+ // Might as well blank our current topic value
+ setchanneltopic(channels, tokens[3], "");
+ // Set the topic timestamp to 0 which we use to determine an "unset" topic when new clients connect
+ setchanneltopicwhotime(channels, tokens[3], "", "0");
+ // Server 332 (RPL_TOPIC) set the channel topic
+ } else if (strncmp(tokens[1], "332", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 332 (RPL_TOPIC) found, extracting topic and storing in channel struct.\n");
+ // Need to extract the final parameter as topics can have spaces
+ // Copy to a temporary string so we still have the original in case we need it
+ char *topiccopy = strdup(str);
+ extractfinalparameter(topiccopy);
+ setchanneltopic(channels, tokens[3], topiccopy);
+ free(topiccopy);
+ // Server 333 (RPL_TOPICWHOTIME) set the channel topic setter and the time it was set
+ } else if (strncmp(tokens[1], "333", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 333 (RPL_TOPICWHOTIME) found, extracting who and when, and storing in channel struct.\n");
+ setchanneltopicwhotime(channels, tokens[3], tokens[4], tokens[5]);
+ // Server 353 (RPL_NAMREPLY), relay to all clients if we've just JOINed the channel, or relay to any clients
+ // who were pending RPL_NAMREPLYs if it's an existing channel.
+ } else if (strncmp(tokens[1], "353", strlen(tokens[1])) == 0) {
+ // It must be a new channel and we don't have the NAMES
+ if (!channelgotnames(channels, tokens[4])) {
+ debugprint(DEBUG_FULL, "Server 353 received for a new channel, sending to all clients.\n");
+ // Relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+ } else {
+ // We were already in the channel and have the NAMES
+ debugprint(DEBUG_FULL, "Server 353 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs.\n");
+ // If any clients were pending RPL_NAMREPLYs, send this on to them
+ // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingnames > 0) {
+ debugprint(DEBUG_FULL, "Sending 353 RPL_NAMREPLY for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n", tokens[4], clients[i].fd, clients[i].pendingnames);
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ }
+ }
+ }
+ return 1;
+ // Server 366 (RPL_ENDOFNAMES), relay to all clients if we've just JOINed the channel, or relay to
+ // and decrement from any clients who were waiting on RPL_NAMREPLY if it's an existing channel.
+ } else if (strncmp(tokens[1], "366", strlen(tokens[1])) == 0) {
+ int channelelement;
+ // It must be a new channel and we don't have the NAMES
+ if (!(channelelement = channelgotnames(channels, tokens[3]))) {
+ debugprint(DEBUG_FULL, "Server 366 received for a new channel, sending to all clients and set as got names.\n");
+ // We have the names now!
+ channels[channelindex(channels, tokens[3])].gotnames = 1;
+ // Relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+ } else {
+ // We were already in the channel and have the NAMES
+ // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
+ debugprint(DEBUG_FULL, "Server 366 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs and decrementing their pendingnames count.\n");
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingnames > 0) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And decrement their pendingnames count
+ debugprint(DEBUG_FULL, "Decrementing pendingnames due 366 RPL_ENDOFNAMES to for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n",
+ tokens[3], clients[i].fd, clients[i].pendingnames);
+ clients[i].pendingnames--;
+ debugprint(DEBUG_FULL, "Client with fd '%d' has '%d' pendingnames left.\n", clients[i].fd, clients[i].pendingnames);
+ }
+ }
+ }
+ return 1;
+ }
+
+ // Server TOPIC received? Update our local channel topic info then relay to clients.
+ if (strncmp(tokens[1], "TOPIC", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server TOPIC found and it is: %s with length %zd! Next token is '%s'. Updating our local channel topic info.\n",
+ tokens[0], strlen(tokens[0]), tokens[2]);
+
+ // Set the topic itself
+
+ // Need to extract the final parameter as topics can have spaces
+ // Copy to a temporary string so we still have the original in case we need it
+ char *topiccopy = strdup(str);
+ extractfinalparameter(topiccopy);
+ setchanneltopic(channels, tokens[2], topiccopy);
+
+ // Extract the author and get the current timestamp
+
+ // Extract the topic setter from the prefix (Prefix of ":foo!bar@baz" means "foo" set the topic.)
+ // Copy to a temporary string so we still have the original in case we need it
+ char *prefixcopy = strdup(tokens[0]);
+ extractnickfromprefix(prefixcopy);
+
+ // Get the current time and manipulate it into a C string
+ time_t timenow = time(NULL);
+ int timenowlen = snprintf(NULL, 0, "%ld", timenow);
+ char timenowstr[timenowlen + 1]; // TODO - Make this Year 2038 proof.
+ snprintf(timenowstr, timenowlen + 1, "%ld", timenow);
+
+ // Actually set the author and timestamp
+ setchanneltopicwhotime(channels, tokens[2], prefixcopy, timenowstr);
+
+ // And then finally relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(str, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(str, settings->ircnick, settings->basedir, LOG_TOPIC);
+ }
+
+ free(topiccopy);
+ free(prefixcopy);
+ return 1;
+ }
+
+ // Server PRIVMSG received? Relay to all clients and write to replay log.
+ if (strncmp(tokens[1], "PRIVMSG", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server PRIVMSG found and it is: %s with length %zd! Next token is '%s'. Relaying to all clients.\n",
+ tokens[0], strlen(tokens[0]), tokens[2]);
+
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(str, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(str, settings->ircnick, settings->basedir, LOG_PRIVMSG);
+ }
+
+ return 1;
+ }
+
+ // Server NICK received?
+ // 1. Find out if it was us and change ircnick and nickuserhost if so
+ // 2. Either way, relay to all clients
+ if (strncmp(tokens[1], "NICK", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server NICK found and it is: %s with length %zd! Next token is '%s'. Updating records and relaying to all clients.\n",
+ tokens[0], strlen(tokens[0]), tokens[2]);
+
+ // Was it us?
+ // Copy to a temporary string so we still have the original in case we need it
+ char *svrprefixcopy = strdup(tokens[0]);
+ // Just get the nick for comparison
+ extractnickfromprefix(svrprefixcopy);
+ if (strncmp(ircdstrings->ircnick, svrprefixcopy, strlen(ircdstrings->ircnick)) == 0) {
+ debugprint(DEBUG_FULL, "Server NICK: nick is ours ('%s' vs '%s').\n", svrprefixcopy, ircdstrings->ircnick);
+ // Make a copy of the old nickuserhost for updategreetings() below
+ char *nickuserhostcpy = strdup(ircdstrings->nickuserhost);
+ // Update nickuserhost with the new :nick!user@host
+ updatenickuserhost(ircdstrings->nickuserhost, tokens[2]);
+ debugprint(DEBUG_FULL, "Updated nickuserhost to '%s'.\n", ircdstrings->nickuserhost);
+ // Prepare to update ircnick and greetings strings
+ // Temporary copy of new nickuserhost
+ char *prefixcopy = strdup(ircdstrings->nickuserhost);
+ // Get nick from it
+ extractnickfromprefix(prefixcopy);
+ // Update greeting strings for relaying to new clients
+ updategreetings(ircdstrings->greeting001, ircdstrings->greeting002, ircdstrings->greeting003, ircdstrings->greeting004,
+ ircdstrings->greeting005a, ircdstrings->greeting005b, ircdstrings->greeting005c, ircdstrings->nickuserhost,
+ nickuserhostcpy, tokens[2], ircdstrings->ircnick);
+ // Update our nick
+ strcpy(ircdstrings->ircnick, prefixcopy);
+ debugprint(DEBUG_FULL, "Updated ircnick to '%s'.\n", ircdstrings->ircnick);
+ free(nickuserhostcpy);
+ free(prefixcopy);
+ }
+
+ // Relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ free(svrprefixcopy);
+ return 1;
+ }
+
+ // Server MODE received? See what sort it is and act accordingly.
+ if (strncmp(tokens[1], "MODE", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server MODE found and it is: %s with length %zd! Next token is '%s'. Analysing...\n",
+ tokens[1], strlen(tokens[1]), tokens[2]);
+
+ // 4 tokens could be either our initial mode or a channel mode (or something else - TODO - what else?)
+ if (counter == 4) {
+ // Might be our initial mode (e.g. ":nick MODE nick :+iwz")
+ char comparison[MAXDATASIZE];
+ snprintf(comparison, MAXDATASIZE, ":%s MODE %s :", ircdstrings->ircnick, ircdstrings->ircnick);
+ if (strncmp(str, comparison, strlen(comparison)) == 0) {
+ // Looks like it!
+ debugprint(DEBUG_FULL, "Our initial MODE found (%s), storing for later.\n", tokens[3]);
+ // Store in ircdstrings for when clients connect and relay to current clients.
+ strcpy(ircdstrings->mode, tokens[3]);
+
+ // Relay to all current clients anyway - TODO - Necessary?
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ return 1;
+ }
+
+ // Might be a channel mode (e.g. ":nick!user@host MODE #channel +s")
+ if (tokens[2][0] == '#') {
+ // Looks like it! Tell all clients.
+ debugprint(DEBUG_FULL, "Channel MODE found (%s %s), telling all clients.\n", tokens[2], tokens[3]);
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ return 1;
+ }
+ }
+
+ // Relay to all current clients if not processed by the above
+ sendtoallclients(clients, str, sourcefd, settings);
+
+ return 1;
+ }
+
+ // Server 324 (RPL_CHANNELMODEIS) received? Send to any clients who requested a channel MODE.
+ if (strncmp(tokens[1], "324", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 324 (RPL_CHANNELMODEIS) found and it is: %s with length %zd! Sending to clients who are pending this.\n",
+ tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingchannelmode == 1) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag
+ clients[i].pendingchannelmode = 0;
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 368 (RPL_ENDOFBANLIST) received? Send to any clients who requested a ban MODE query. - TODO - Identify and handle start/middle of ban responses.
+ if (strncmp(tokens[1], "368", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 368 (RPL_ENDOFBANLIST) found and it is: %s with length %zd! Sending to clients who are pending this.\n",
+ tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingban == 1) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag
+ clients[i].pendingban = 0;
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 329 (RPL_CREATIONTIME), 352 (RPL_WHOREPLY), or 315 (RPL_ENDOFWHO) received? Send to any clients who requested a WHO.
+ if (strncmp(tokens[1], "329", strlen(tokens[1])) == 0 || strncmp(tokens[1], "352", strlen(tokens[1])) == 0 || strncmp(tokens[1], "315", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 329 (RPL_CREATIONTIME), 352 (RPL_WHOREPLY), or 315 (RPL_ENDOFWHO) found and it is: %s with length %zd! Sending to clients who are pending one of these.\n",
+ tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingwho == 1 && clients[i].fd) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag if it's 315 (RPL_ENDOFWHO)
+ if (strncmp(tokens[1], "315", strlen(tokens[1])) == 0) {
+ clients[i].pendingwho = 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 321 (RPL_LISTSTART), 322 (RPL_LIST), or 323 (RPL_LISTEND) received? Send to any clients who requested a WHO.
+ if (strncmp(tokens[1], "321", strlen(tokens[1])) == 0 || strncmp(tokens[1], "322", strlen(tokens[1])) == 0 || strncmp(tokens[1], "323", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 321 (RPL_LISTSTART), 322 (RPL_LIST), or 323 (RPL_LISTEND) found and it is: %s with length %zd! Sending to clients who are pending one of these.\n",
+ tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendinglist == 1 && clients[i].fd) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag if it's 323 (RPL_LISTEND)
+ if (strncmp(tokens[1], "323", strlen(tokens[1])) == 0) {
+ clients[i].pendinglist = 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 307 (RPL_SUSERHOST), 311 (RPL_WHOISUSER), 312 (RPL_WHOISSERVER), 313 (RPL_WHOISOPERATOR), 317 (RPL_WHOISIDLE),
+ // 319 (RPL_WHOISCHANNELS), 320 (RPL_WHOISSPECIAL), 671 (RPL_WHOISSECURE), or 318 (RPL_ENDOFWHOIS) received?
+ // Send to any clients who requested a WHOIS.
+ if (strncmp(tokens[1], "307", strlen(tokens[1])) == 0 || strncmp(tokens[1], "311", strlen(tokens[1])) == 0 ||
+ strncmp(tokens[1], "312", strlen(tokens[1])) == 0 || strncmp(tokens[1], "313", strlen(tokens[1])) == 0 ||
+ strncmp(tokens[1], "317", strlen(tokens[1])) == 0 || strncmp(tokens[1], "319", strlen(tokens[1])) == 0 ||
+ strncmp(tokens[1], "320", strlen(tokens[1])) == 0 || strncmp(tokens[1], "671", strlen(tokens[1])) == 0 ||
+ strncmp(tokens[1], "318", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 307 RPL_SUSERHOST, 311 RPL_WHOISUSER, 312 RPL_WHOISSERVER, 313 (RPL_WHOISOPERATOR), 317 RPL_WHOISIDLE, "
+ "319 RPL_WHOISCHANNELS, 320 (RPL_WHOISSPECIAL), 671 (RPL_WHOISSECURE), or 318 RPL_ENDOFWHOIS found and it is: "
+ "%s with length %zd! Sending to clients who are pending one of these.\n", tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingwhois == 1 && clients[i].fd) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag if it's 318 RPL_ENDOFWHOIS
+ if (strncmp(tokens[1], "318", strlen(tokens[1])) == 0) {
+ clients[i].pendingwhois = 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 314 (RPL_WHOWASUSER), 406 (ERR_WASNOSUCHNICK), or 369 (RPL_ENDOFWHOWAS) received?
+ // Send to any clients who requested a WHOWAS.
+ if (strncmp(tokens[1], "314", strlen(tokens[1])) == 0 || strncmp(tokens[1], "406", strlen(tokens[1])) == 0 || strncmp(tokens[1], "369", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "314 (RPL_WHOWASUSER), 406 (ERR_WASNOSUCHNICK), or 369 (RPL_ENDOFWHOWAS) "
+ "found and it is: %s with length %zd! Sending to clients who are pending one of these.\n", tokens[1], strlen(tokens[1]));
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingwhowas == 1 && clients[i].fd) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // And clear the pending flag if it's 369 RPL_ENDOFWHOWAS
+ if (strncmp(tokens[1], "369", strlen(tokens[1])) == 0) {
+ clients[i].pendingwhowas = 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ // Server 312 (RPL_WHOISSERVER) received? Check to see if anyone was pending a WHOIS or a WHOWAS and send to them, if not send to everyone.
+ if (strncmp(tokens[1], "312", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 312 (RPL_WHOISSERVER) found and it is: %s with length %zd! Sending to clients who are pending this or to everyone if nobody is.\n",
+ tokens[1], strlen(tokens[1]));
+
+ int waspending = 0;
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingwhois == 1 || clients[i].pendingwhowas == 1) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // Note that we were pending this
+ waspending = 1;
+ }
+ }
+
+ // If no client was pending this, send to everyone
+ if (!waspending) {
+ sendtoallclients(clients, str, 0, settings);
+ }
+
+ return 1;
+ }
+
+
+ // Server 401 (ERR_NOSUCHNICK) received? Check to see if anyone was pending a WHOIS and send to them,
+ // if not send to everyone (401 was probably in reply to something else like a PRIVMSG).
+ if (strncmp(tokens[1], "401", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server 401 (ERR_NOSUCHNICK) found and it is: %s with length %zd! Sending to clients who are pending this or to everyone if nobody is.\n",
+ tokens[1], strlen(tokens[1]));
+
+ int waspending = 0;
+
+ // Relay to all pending clients
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingwhois == 1) {
+ sendtoclient(clients[i].fd, str, clients, settings, 0);
+ // Note that we were pending this
+ waspending = 1;
+ }
+ }
+
+ // If no client was pending this, send to everyone
+ if (!waspending) {
+ sendtoallclients(clients, str, 0, settings);
+ }
+
+ return 1;
+ }
+
+ // Server 432 (ERR_ERRONEUSNICKNAME) or 433 (ERR_NICKNAMEINUSE) received? See which nick we're on and try another.
+ // (But only if we're not already registered with the real IRC server.)
+ if ((strncmp(tokens[1], "432", strlen(tokens[1])) == 0 || strncmp(tokens[1], "433", strlen(tokens[1])) == 0) && !strlen(ircdstrings->greeting004)) {
+ debugprint(DEBUG_SOME, "Server 432 (ERR_ERRONEUSNICKNAME) or 433 (ERR_NICKNAMEINUSE) found and it is: %s with length %zd! Trying another nick...\n",
+ tokens[1], strlen(tokens[1]));
+
+ // Try to get nick2 and nick3 from the configuration file
+ char nick2[MAXNICKLENGTH];
+ char nick3[MAXNICKLENGTH];
+ if (!getconfstr("nick2", settings->conffile, nick2)) {
+ nick2[0] = '\0';
+ }
+ if (!getconfstr("nick3", settings->conffile, nick3)) {
+ nick3[0] = '\0';
+ }
+
+ // Do we have both a nick2 and a nick3? (And not tried autonick yet.)
+ if (nick2[0] && nick3[0] && !ircdstrings->autonicknum) {
+ // Has nick3 already been tried?
+ if (strncmp(ircdstrings->ircnick, nick3, strlen(settings->ircnick)) == 0) {
+ // Then try autonick
+ tryautonick(ircdstrings);
+ // Has nick2 already been tried?
+ } else if (strncmp(ircdstrings->ircnick, nick2, strlen(settings->ircnick)) == 0) {
+ // Then try nick3
+ debugprint(DEBUG_SOME, "Trying nick3, nick2 was already tried.\n");
+ strcpy(ircdstrings->ircnick, nick3);
+ // Have neither been tried?
+ } else {
+ // Then try nick2
+ debugprint(DEBUG_SOME, "Trying nick2, nick3 is also configured.\n");
+ strcpy(ircdstrings->ircnick, nick2);
+ }
+ // Do we only have a nick2? (And not tried autonick yet.)
+ } else if (nick2[0] && !ircdstrings->autonicknum) {
+ // Has it already been tried?
+ if (strncmp(ircdstrings->ircnick, nick2, strlen(settings->ircnick)) == 0) {
+ // Then try autonick
+ tryautonick(ircdstrings);
+ } else {
+ // Then try it
+ debugprint(DEBUG_SOME, "Trying nick2, nick3 is not configured.\n");
+ strcpy(ircdstrings->ircnick, nick2);
+ }
+ // Do we have neither? (Or have already started autonick.)
+ } else {
+ // Then try autonick
+ tryautonick(ircdstrings);
+ }
+
+ // Set whichever one we settled on in the settings in case we reference settings later
+ strcpy(settings->ircnick, ircdstrings->ircnick);
+
+ // Try it with the server
+ char outgoingmsg[MAXDATASIZE];
+ snprintf(outgoingmsg, MAXDATASIZE, "NICK %s", ircdstrings->ircnick);
+ // sourcefd = 0 as this is a trusted message
+ sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
+
+ return 1;
+ }
+
+ // Server CAP received?
+ if (strncmp(tokens[1], "CAP", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server CAP found and it is: %s with length %zd! Analysing...\n", tokens[1], strlen(tokens[1]));
+ // If the server said "CAP <ournick> ACK :multi-prefix" then it must have approved our CAP multi-prefix request
+ if (counter == 5) {
+ if (strncmp(tokens[2], ircdstrings->ircnick, strlen(tokens[2])) == 0 &&
+ strncmp(tokens[3], "ACK", strlen(tokens[3])) == 0 &&
+ strncmp(tokens[4], ":multi-prefix", strlen(tokens[4])) == 0) {
+ ircdstrings->capmultiprefix = 1;
+ }
+ }
+ // We didn't handle it
+ debugprint(DEBUG_FULL, "Unhandled server CAP response.\n");
+ }
+
+ // Server NOTICE received? Handle and log if it's from a user, otherwise let pass through to default handler.
+ if (strncmp(tokens[1], "NOTICE", strlen(tokens[1])) == 0) {
+ debugprint(DEBUG_FULL, "Server NOTICE found and it is: %s with length %zd! Analysing...\n", tokens[1], strlen(tokens[1]));
+ // If the first token is a nick!user@host then it's probably from a user
+ if (strstr(tokens[0], "!") != NULL && strstr(tokens[0], "@") != NULL) {
+ debugprint(DEBUG_FULL, "Server NOTICE appears to be from a user, sending to all clients and logging.\n");
+
+ sendtoallclients(clients, str, 0, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(str, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(str, settings->ircnick, settings->basedir, LOG_PRIVMSG);
+ }
+
+ return 1;
+ } else {
+ debugprint(DEBUG_FULL, "Server NOTICE does not appear to be from a user, passing through.\n");
+ }
+ }
+ }
+
+ // We didn't process anything so return 0
+ return 0;
+}
+
+// Process an IRC message that came from a client.
+// Return 1 if we processed it, or 0 if we didn't.
+int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings,
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter) {
+ // PASS received? User is trying to log in, check their password.
+ if (strncasecmp(tokens[0], "PASS", strlen(tokens[0])) == 0) {
+ if (checkpassword(tokens[1], settings->conffile)) {
+ debugprint(DEBUG_FULL, "Password accepted! Setting fd %d to authenticated.\n", sourcefd);
+ // Find the client in the clients array and set them as authenticated
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].fd == sourcefd) {
+ // Found client in array, set to authenticated
+ clients[i].authed = 1;
+ debugprint(DEBUG_FULL, "Found and authenticated fd in arr_authed.\n");
+ // Alert other clients about the successful authentication
+ char alertmsg[MAXDATASIZE];
+ if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: new client with fd %d has successfully authenticated.", ircdstrings->ircnick, sourcefd)) {
+ fprintf(stderr, "Error while preparing authentication success NOTICE!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing authentication success NOTICE!\n");
+ alertmsg[0] = '\0';
+ }
+ // "except" the current fd - we can use this as "except/sourcefd" since we set them as authed just above
+ sendtoallclients(clients, alertmsg, sourcefd, settings);
+ }
+ }
+ } else {
+ debugprint(DEBUG_SOME, "Password rejected, disconnecting fd %d.\n", sourcefd);
+ disconnectclient(sourcefd, clients, ircdstrings, settings);
+ // Alert other clients about the failed authentication
+ char alertmsg[MAXDATASIZE];
+ if (!snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :blabouncer: new client with fd %d has failed to authenticate.", ircdstrings->ircnick, sourcefd)) {
+ fprintf(stderr, "Error while preparing authentication failure NOTICE!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing authentication failure NOTICE!\n");
+ alertmsg[0] = '\0';
+ }
+ // "except" 0 since we trust this message
+ sendtoallclients(clients, alertmsg, 0, settings);
+ }
+
+ return 1;
+ }
+
+ // CAP received? Clients can send CAP before PASS so we have to deal with this even if they are not authenticated yet.
+ if (strncasecmp(tokens[0], "CAP", strlen(tokens[0])) == 0) {
+ // But only do something if the real server told us it had a CAP (only multi-prefix for now)
+ if (ircdstrings->capmultiprefix == 1) {
+ debugprint(DEBUG_FULL, "Client CAP received and the server supports CAPs, continuing.\n");
+ } else {
+ debugprint(DEBUG_FULL, "Client CAP received but the server doesn't support CAPs, returning.\n");
+ return 1;
+ }
+ // Get the real IRC server name from greeting001
+ // They are now pending CAP negotiation
+ clients[arrindex(clients, sourcefd)].pendingcap = 1;
+ char outgoingmsg[MAXDATASIZE];
+ // If client is requesting CAP list, send it...
+ if (strncasecmp(tokens[1], "LS", strlen(tokens[1])) == 0) {
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s CAP * LS :multi-prefix", ircdstrings->ircdname)) {
+ fprintf(stderr, "Error while preparing CAP LS response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing CAP LS response!\n");
+ outgoingmsg[0] = '\0';
+ }
+ // ...even if unauthenticated
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 1);
+ return 1;
+ // If client is requesting a CAP...
+ } else if (strncasecmp(tokens[1], "REQ", strlen(tokens[1])) == 0) {
+ // ...and it is "multi-prefix", send it
+ if (strncasecmp(tokens[2], ":multi-prefix", strlen(tokens[2])) == 0) {
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s CAP %s ACK :multi-prefix ", ircdstrings->ircdname, ircdstrings->ircnick)) {
+ fprintf(stderr, "Error while preparing CAP ACK response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing CAP ACK response!\n");
+ outgoingmsg[0] = '\0';
+ }
+ // ...even if unauthenticated
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 1);
+ return 1;
+ }
+ // If client is finishing CAP negotiation then mark them as so
+ } else if (strncasecmp(tokens[1], "END", strlen(tokens[1])) == 0) {
+ clients[arrindex(clients, sourcefd)].pendingcap = -1;
+ }
+ }
+
+ // We're past PASS in the list of possible commands, so ignore
+ // anything else the client says if they are not authenticated yet.
+ if (!clients[arrindex(clients, sourcefd)].authed) {
+ debugprint(DEBUG_CRIT, "Ignoring client command '%s' from sourcefd '%d' as not authenticated yet.\n", tokens[0], sourcefd);
+ return 1;
+ }
+
+ // USER received and not pending CAP negotiation during registration?
+ // Or client has just finished negotiating CAP (pendingcap = -1)?
+ // If so, assume this is a new client connecting and catch them on up on the state
+ if ((strncasecmp(tokens[0], "USER", strlen(tokens[0])) == 0 && clients[arrindex(clients, sourcefd)].pendingcap == 0) || clients[arrindex(clients, sourcefd)].pendingcap == -1) {
+ // Somewhere to store the several strings we will need to build and send
+ char outgoingmsg[MAXDATASIZE]; // String to send to client
+
+ // If registering then they must no longer be pending CAP negotiation
+ clients[arrindex(clients, sourcefd)].pendingcap = 0;
+
+ // Tell the client to go away if we aren't registered with the real server yet as defined by the last greeting not being set yet
+ if (!strlen(ircdstrings->greeting004)) {
+ sendtoclient(sourcefd, "Sorry, we aren't registered with a real IRC server yet.", clients, settings, 0);
+ disconnectclient(sourcefd, clients, ircdstrings, settings);
+ return 1;
+ }
+
+ // Send IRC greeting strings (001/RPL_WELCOME, 002/RPL_YOURHOST, 003/RPL_CREATED, 004/RPL_MYINFO, 005/RPL_ISUPPORT) to client
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting001);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting002);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting003);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ if (ircdstrings->greeting005a[0]) {
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005a);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+ if (ircdstrings->greeting005b[0]) {
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005b);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+ if (ircdstrings->greeting005c[0]) {
+ snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting005c);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ // Send our own greeting message
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Welcome to blabouncer!", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using \"/QUOTE BLABOUNCER\"", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Get the channel count so we can enumerate over all channels.
+ // Storing separately so we can skip over blank channels.
+ int channelcount = getchannelcount(channels);
+ // Set the client as pending RPL_NAMREPLYs for 'channelcount' channels
+ debugprint(DEBUG_FULL, "Setting pendingnames to '%d' for client with fd '%d'.\n", channelcount, sourcefd);
+ clients[arrindex(clients, sourcefd)].pendingnames = channelcount;
+
+ // Get client to join channels, and tell client about those channels
+ for (int i = 0; i < channelcount; i++) {
+ debugprint(DEBUG_FULL, "JOINing channel[%d] out of %d.\n", i, channelcount);
+ // Skip this one and increment channelcount if it's a blank channel
+ if (!channels[i].name[0]) {
+ debugprint(DEBUG_FULL, "Skipping channel[%d], incrementing channelcount.\n", i);
+ channelcount++;
+ continue;
+ }
+
+ // Get client to join channels
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses!\n");
+ return 0;
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Send topic (or lack thereof) to client
+ // If there isn't one set (we guess this if topic timestamp is 0), send 331 RPL_NOTOPIC
+ if (strncmp(channels[i].topicwhen, "0", 1) == 0) {
+ // Prepare the no topic message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 331 %s %s :No topic is set.", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 331 RPL_NOTOPIC!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 331 RPL_NOTOPIC!\n");
+ return 0;
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ // If there is one set, send 332 RPL_TOPIC and 333 RPL_TOPICWHOTIME
+ } else {
+ // Prepare the topic message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 332 %s %s :%s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topic)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 332 RPL_TOPIC!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 332 RPL_TOPIC!\n");
+ return 0;
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Next prepare the topic who/when message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 333 %s %s %s %s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topicwho, channels[i].topicwhen)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 333 RPL_TOPICWHOTIME!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing USER just connected, channel JOIN responses, 333 RPL_TOPICWHOTIME!\n");
+ return 0;
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ // Get the latest RPL_NAMREPLY for this channel to relay to the client when it arrives
+ char namesreq[MAXDATASIZE];
+ snprintf(namesreq, MAXDATASIZE, "NAMES %s", channels[i].name);
+ sendtoserver(server_ssl, namesreq, strlen(namesreq), 0, clients, settings);
+ }
+
+ // Send our mode to the client (if we have one)
+ if (strlen(ircdstrings->mode) > 0) {
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s MODE %s %s", ircdstrings->ircnick, ircdstrings->ircnick, ircdstrings->mode)) {
+ fprintf(stderr, "Error while preparing USER just connected, MODE response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing USER just connected, MODE response!\n");
+ return 0;
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ // Set the client as registered
+ clients[arrindex(clients, sourcefd)].registered = 1;
+
+ // Catch the client up with the default number of seconds of replay
+ if (!doreplay(sourcefd, settings->replayseconds, clients, settings, ircdstrings, channels)) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ return 1;
+ }
+
+ // Pretty much ignore anything else the client says if it's not registered yet,
+ // as the first thing we want to hear is either PASS or USER
+ if (!clients[arrindex(clients, sourcefd)].registered) {
+ debugprint(DEBUG_SOME, "Ignoring client command '%s' from sourcefd '%d' as not registered yet.\n", tokens[0], sourcefd);
+ return 1;
+ }
+
+ // Client PING received? If so, send a PONG back with the next element as the argument.
+ if (strncasecmp(tokens[0], "PING", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0]));
+
+ char outgoingmsg[MAXDATASIZE]; // String to send to client
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "PONG %s", tokens[1])) { // TODO - Make sure tokens[1] actually has a token
+ fprintf(stderr, "Error while preparing PONG response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing PONG response!\n");
+ outgoingmsg[0] = '\0';
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // We processed something so return true
+ return 1;
+ }
+
+ // TODO - Ignoring CAP for now so as not to confuse other clients, but we should probably query the server then relay the response to the client
+ if (strncasecmp(tokens[0], "CAP", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client CAP found and it is: %s with length %zd! Ignoring completely for now - TODO - do something useful!\n", tokens[0], strlen(tokens[0]));
+ return 1;
+ }
+
+ // Just send NICK to server and let it change all clients' nicks if needed
+ if (strncasecmp(tokens[0], "NICK", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client NICK found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // If PRIVMSG received, send to server, but also reformat and send to all other clients and log to replay file.
+ if (strncasecmp(tokens[0], "PRIVMSG", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client PRIVMSG found and it is: %s with length %zd! Sending to server then back to other clients...\n", tokens[0], strlen(tokens[0]));
+ // Send original request straight to server
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+
+ // If it seems to be a CTCP VERSION request, just send to the server (CTCP requests are delimited with \1)
+ if (counter == 3 && strncmp(tokens[2], ":\1VERSION\1", strlen(tokens[2])) == 0) {
+ debugprint(DEBUG_FULL, "Client PRIVMSG looked like a CTCP VERSION request, so not sending to other clients.\n");
+ return 1;
+ }
+
+ // Rebuild to full PRIVMSG string and relay to all other clients
+ char outgoingmsg[MAXDATASIZE]; // String to send to client
+
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) {
+ fprintf(stderr, "Error while preparing PRIVMSG relay from another bouncer client.\n");
+ debugprint(DEBUG_CRIT, "Error while preparing PRIVMSG relay from another bouncer client.\n");
+ return 0;
+ }
+ // Send to all except source client
+ sendtoallclients(clients, outgoingmsg, sourcefd, settings);
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(outgoingmsg, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(outgoingmsg, settings->ircnick, settings->basedir, LOG_PRIVMSG);
+ }
+
+ return 1;
+ }
+
+ // Just send JOIN to server and let it talk back to clients as required
+ if (strncasecmp(tokens[0], "JOIN", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client JOIN found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Don't do anything with QUIT, just let the client go - TODO: Let another clients know with a NOTICE or something
+ // A client has QUIT, so disconnect (close) them and don't do anything else for now - TODO: Let another clients know with a NOTICE or something
+ if (strncasecmp(tokens[0], "QUIT", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client QUIT found from fd %d and it is: %s with length %zd! Disconnecting that fd.\n", sourcefd, tokens[0], strlen(tokens[0]));
+ disconnectclient(sourcefd, clients, ircdstrings, settings);
+ return 1;
+ }
+
+ // Just send PART to server and let it talk back to clients as required
+ if (strncasecmp(tokens[0], "PART", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client PART found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Just send TOPIC to server and let it talk back to clients as required
+ if (strncasecmp(tokens[0], "TOPIC", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client TOPIC found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Take note of what sort of MODE was requested, mark the client as waiting for the reply (so not all clients get it)
+ // Send it to the server either way and let it talk back to the requesting client as required
+ if (strncasecmp(tokens[0], "MODE", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client MODE found and it is: %s with length %zd! Analysing...\n", tokens[0], strlen(tokens[0]));
+ // Is it a ban MODE request (MODE #channel b)?
+ if (counter >= 3 && strncmp(tokens[2], "b", strlen("b")) == 0) {
+ debugprint(DEBUG_FULL, "Ban MODE request received, marking as pending.\n");
+ clients[arrindex(clients, sourcefd)].pendingban = 1;
+ } else if (counter == 2) {
+ // Assume a normal channel mode request (MODE #channel) - TODO - What if it isn't!?
+ debugprint(DEBUG_FULL, "Assuming channel MODE request received, marking as pending.\n");
+ clients[arrindex(clients, sourcefd)].pendingchannelmode = 1;
+ }
+
+ // Either way, send it on the server
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // WHO requested, mark the client as waiting for the reply (so not all clients get it) and send it on the server
+ if (strncasecmp(tokens[0], "WHO", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client WHO found and it is: %s with length %zd! Marking as pending.\n", tokens[0], strlen(tokens[0]));
+ clients[arrindex(clients, sourcefd)].pendingwho = 1;
+
+ // Either way, send it on the server
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // LIST requested, mark the client as waiting for the reply (so not all clients get it) and send it on the server
+ if (strncasecmp(tokens[0], "LIST", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client LIST found and it is: %s with length %zd! Marking as pending.\n", tokens[0], strlen(tokens[0]));
+ clients[arrindex(clients, sourcefd)].pendinglist = 1;
+
+ // Either way, send it on the server
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Client WHOIS received, send straight on to server and mark the client as pending the response
+ if (strncasecmp(tokens[0], "WHOIS", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client WHOIS found and it is: %s with length %zd! Sending to server and setting client as pending.\n", tokens[0], strlen(tokens[0]));
+ clients[arrindex(clients, sourcefd)].pendingwhois = 1;
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Client WHOWAS received, send straight on to server and mark the client as pending the response
+ if (strncasecmp(tokens[0], "WHOWAS", strlen(tokens[0])) == 0) {
+ debugprint(DEBUG_FULL, "Client WHOWAS found and it is: %s with length %zd! Sending to server and setting client as pending.\n", tokens[0], strlen(tokens[0]));
+ clients[arrindex(clients, sourcefd)].pendingwhowas = 1;
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // Client NOTICE received
+ if (strncasecmp(tokens[0], "NOTICE", strlen(tokens[0])) == 0) {
+
+ // Rebuild to full NOTICE string including our nick!user@host for logging
+ char fullmsg[MAXDATASIZE];
+
+ if (!snprintf(fullmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) {
+ fprintf(stderr, "Error while preparing NOTICE string for logging.\n");
+ debugprint(DEBUG_CRIT, "Error while preparing NOTICE string for logging.\n");
+ return 0;
+ }
+
+ // Write to replay log if replay logging enabled
+ if (settings->replaylogging) {
+ writereplayline(fullmsg, settings->basedir);
+ }
+
+ // Write to normal log if logging enabled
+ if (settings->logging) {
+ logline(fullmsg, settings->ircnick, settings->basedir, LOG_PRIVMSG);
+ }
+
+ // If it's a CTCP VERSION response then only send to the server (CTCP requests are delimited with \1)
+ if (counter >= 3 && strncmp(tokens[2], ":\1VERSION", strlen(tokens[2])) == 0) {
+ debugprint(DEBUG_FULL, "Client NOTICE looked like a CTCP VERSION response, so just sending to the server.\n");
+ sendtoserver(server_ssl, str, strlen(str), sourcefd, clients, settings);
+ return 1;
+ }
+
+ // If it wasn't a CTCP VERSION response, then let this fall through to the default unhandled action by not returning here
+ }
+
+ // Custom BLABOUNCER command received
+ // Case insensitive comparisons here since clients won't be recognising and uppercasing these commands
+ if (strncasecmp(tokens[0], "BLABOUNCER", strlen(tokens[0])) == 0) {
+ char outgoingmsg[MAXDATASIZE];
+ debugprint(DEBUG_FULL, "Client BLABOUNCER found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
+ // REPLAY received, send the requested length of replay time to the client
+ if (strncasecmp(tokens[1], "REPLAY", strlen("REPLAY")) == 0) {
+ debugprint(DEBUG_FULL, "Client BLABOUNCER REPLAY (custom blabouncer command) found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
+
+ // Split the request into days:hours:minutes:seconds
+
+ // Track which colon-separated token within this request we're on
+ int timecounter = 0;
+
+ // Build array of colon-separated tokens
+ char timetokens[MAXTOKENS][MAXDATASIZE];
+ // Copy to a temporary string so we still have the original in case it's not processed
+ char *timestrcopy = strdup(tokens[2]);
+ // Keep track of initial pointer for free()ing later
+ char *timestrcopyPtr = timestrcopy;
+
+ char *timetoken;
+ while ((timetoken = strsep(&timestrcopy, ":")) != NULL) {
+ if (*timetoken == '\0') continue; // Skip consecutive matches
+ if (timecounter >= MAXTOKENS) break; // Too many tokens
+ debugprint(DEBUG_FULL, "Time token: \"%s\", length %zd.\n", timetoken, strlen(timetoken));
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(timetokens[timecounter], timetoken, strlen(timetoken) + 1);
+ timecounter++;
+ }
+
+ // Make sure we don't have more than four (d:h:m:s) components
+ if (timecounter > 4) {
+ debugprint(DEBUG_SOME, "Too many time components requested by REPLAY command. Telling client.\n");
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Too many time components requestd by REPLAY command. Expected up to four (days:hours:minutes:seconds).", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ free(timestrcopyPtr);
+ return 1;
+ }
+
+ // Make sure all the components are numbers
+ for (int i = 0; i < timecounter; i++) {
+ // Temporary number and pointer for checking errors
+ long check;
+ char *str_end;
+ errno = 0;
+ check = strtol(timetokens[i], &str_end, 10);
+ if (str_end == timetokens[i] || ((check == LONG_MAX || check == LONG_MIN) && errno == ERANGE)) {
+ debugprint(DEBUG_SOME, "Invalid number '%s' requested by REPLAY command. Telling client.\n", timetokens[i]);
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Invalid number '%s' requested by REPLAY command.", ircdstrings->ircnick, timetokens[i])) {
+ fprintf(stderr, "Error while preparing REPLAY invalid number response!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing REPLAY invalid number response!\n");
+ outgoingmsg[0] = '\0';
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ free(timestrcopyPtr);
+ return 1;
+ }
+ }
+
+ // How many seconds we're going to replay
+ int replayseconds = 0;
+
+ // If d:h:m:s provided
+ if (timecounter == 4) {
+ replayseconds += 86400 * strtol(timetokens[0], NULL, 10);
+ replayseconds += 3600 * strtol(timetokens[1], NULL, 10);
+ replayseconds += 60 * strtol(timetokens[2], NULL, 10);
+ replayseconds += strtol(timetokens[3], NULL, 10);
+ }
+ // If h:m:s provided
+ if (timecounter == 3) {
+ replayseconds += 3600 * strtol(timetokens[0], NULL, 10);
+ replayseconds += 60 * strtol(timetokens[1], NULL, 10);
+ replayseconds += strtol(timetokens[2], NULL, 10);
+ }
+ // If m:s provided
+ if (timecounter == 2) {
+ replayseconds += 60 * strtol(timetokens[0], NULL, 10);
+ replayseconds += strtol(timetokens[1], NULL, 10);
+ }
+ // If s provided
+ if (timecounter == 1) {
+ replayseconds += strtol(timetokens[0], NULL, 10);
+ }
+
+ debugprint(DEBUG_FULL, "Replaying '%s' which is '%d' seconds.\n", tokens[2], replayseconds);
+
+ if (!doreplay(sourcefd, replayseconds, clients, settings, ircdstrings, channels)) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+ free(timestrcopyPtr);
+ return 1;
+ // QUIT received, send QUIT message to server and exit cleanly
+ } else if (strncasecmp(tokens[1], "QUIT", strlen("QUIT")) == 0) {
+ debugprint(DEBUG_SOME, "Client BLABOUNCER QUIT found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
+ // Combine "QUIT :" with any optional quit message the user provided
+ if (counter > 2) {
+ // Any optional quit message comes 16 characters after the start of the string ("BLABOUNCER QUIT " is 16 characters)
+ cleanexit(server_ssl, clients, sourcefd, ircdstrings, settings, str + 16);
+ } else {
+ cleanexit(server_ssl, clients, sourcefd, ircdstrings, settings, "");
+ }
+
+ // Unrecognised BLABOUNCER command received, send some help instructions
+ } else {
+ debugprint(DEBUG_SOME, "Client BLABOUNCER unrecognised command found and it is: %s with length %zd! Sending a help message.\n", tokens[1], strlen(tokens[1]));
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unrecognised BLABOUNCER command received. Valid commands are:", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstrings->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ return 1;
+ }
+ }
+
+ // We didn't process anything so return 0
+ return 0;
+}
diff --git a/message.h b/message.h
new file mode 100644
index 0000000..349a764
--- /dev/null
+++ b/message.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of blabouncer (https://www.blatech.co.uk/l_bratch/blabouncer).
+ * Copyright (C) 2019 Luke Bratch <luke@bratch.co.uk>.
+ *
+ * Blabouncer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * Blabouncer is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with blabouncer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MESSAGE_H_INCLUDED
+#define MESSAGE_H_INCLUDED
+
+#include "functions.h"
+#include "config.h"
+#include "logging.h"
+
+#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)
+
+// Process an IRC message that came from the real server.
+// Return 1 if we processed it, or 0 if we didn't.
+int processservermessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings,
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter);
+
+// Process an IRC message that came from a client.
+// Return 1 if we processed it, or 0 if we didn't.
+int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstrings *ircdstrings,
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter);
+
+#endif