diff options
author | Luke Bratch <luke@bratch.co.uk> | 2019-06-10 21:17:10 +0100 |
---|---|---|
committer | Luke Bratch <luke@bratch.co.uk> | 2019-06-10 21:17:10 +0100 |
commit | c84d8c493ccdc840a866a9f51f10fe6b1f2bc377 (patch) | |
tree | b064e1f554b19378beb0909c78061001b331eb3a /blabouncer.c | |
parent | 853efa64ff8ad6ebde47d1265f8b13e954a0d2f0 (diff) |
Refactoring - split giant processircmessage() switch statement into separate server and client functions in message.h/message.c.
Diffstat (limited to 'blabouncer.c')
-rw-r--r-- | blabouncer.c | 1313 |
1 files changed, 6 insertions, 1307 deletions
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(×trcopy, ":")) != 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); |