diff options
-rw-r--r-- | blabouncer.c | 445 | ||||
-rw-r--r-- | functions.c | 77 | ||||
-rw-r--r-- | functions.h | 6 |
3 files changed, 508 insertions, 20 deletions
diff --git a/blabouncer.c b/blabouncer.c index 39e8166..6728fcc 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -1,6 +1,14 @@ // TODO FOR TOMORROW: // - FIGURE OUT WHEN WE'RE REGISTERED (NICK and USER from user are replied to with commands 001, 002, 003, and 004 from the ircd when registration successful) // - int registered = 0; already created to track when this has happened (assuming still want to do it this way) +// - handle nick in use +// - Might need to change channel struct nicks to be channel struct user struct with its own nick/modes/etc. +// - Do we actually need to store the modes in the channel struct? +// - Get CAP from server and relay to client +// - Add blabouncer MOTD (375, 372, 376) +// - Need to delete channels from struct when PARTing them +// - Joining while connected doesn't properly join other clients +// // Example WHOIS reply: // BOUNCER-SERVER RECEIVED: :irc.tghost.co.uk 307 blabounce l_bratch :is identified for this nick @@ -13,6 +21,7 @@ #include <errno.h> #include <string.h> #include <netdb.h> +#include <time.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> @@ -31,19 +40,59 @@ #define MAXCLIENTS 32 // maximum number of clients that can connect to the bouncer at a time #define MAXTOKENS 100 // maximum number of (CRLF or space) separated tokens per server response we expect (TODO - check this is reasonable) (CRLF and spaces just grouped here due to laziness) #define MAXPONGSIZE 32 // let's assume PING/PONG responses can't be larger than this (TODO - check this [it's totally made up]!) +#define MAXCHANNELS 1024 // let's assume 1024 is reasonable for now (it's configured per IRCd) +#define MAXCHANLENGTH 50 // 50 according to RFC 2811 and RFC 2822 +#define MAXCHANUSERS 8192 // Randomly picked (TODO - is there an actual maximum number of users per channel?) +#define MAXNICKLENGTH 64 // Randomly picked (TODO - is there an actual maximum number (ignoring the RFC preference of 9)?) #define IRCNICK "blabounce" // TODO: change this to a config option! #define IRCUSER "blabounce" // TODO: change this to a config option! #define IRCREALNAME "Mr Bla Bouncer" // TODO: change this to a config option! +#define IRCUSERHOST "tghost.co.uk" // TODO: change this to a config option! + +struct channel { + char name[MAXCHANLENGTH]; + char topic[MAXDATASIZE]; // TODO - Is there a particular maximum topic length? // TODO - DO WE NEED THIS!? What a twist! + char topicwho[MAXNICKLENGTH]; + char topicwhen[11]; // 32-bit unixtime is up to 10 characters (+1 for null char) // TODO - Make this Year 2038 proof + char modes[MAXDATASIZE]; // TODO - Is there a particular maximum modes length? // TODO - DO WE NEED THIS!? What a twist! + char nicks[MAXCHANUSERS][MAXNICKLENGTH]; // TODO - Need to modify this as people leave/join, not just when we first join + char namestype[2]; // Single character (@/*/=) (+1 for null char) // TODO - Is this a sensible name? +}; + +struct ircdstrings { + char greeting001[MAXDATASIZE]; + char greeting002[MAXDATASIZE]; + char greeting003[MAXDATASIZE]; + char greeting004[MAXDATASIZE]; + char ircdname[MAXDATASIZE]; + char nickuserhost[MAXDATASIZE]; // TODO - Make sure this changes when nick changes, if that's meant to happen. +}; int debugmode = 0; +// Send whatever string to a specific client by providing the FD +int sendtoclient(int fd, char *str) { + appendcrlf(str); // Do this just before sending so callers don't need to worry about it + + printf("sendtoclient(): sending \"%s\" (length %zd) to client with fd %d.\n", str, strlen(str), fd); + if (send(fd, str, strlen(str), 0) == -1) { + perror("error: sendtoclient() send()\n"); + return 0; + } + + return 1; +} + // Relay/send message to all clients (optionally except one) // "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all) -int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int str_len, int except) { +// TODO - is passing str_len useful if we're appendcrlfing and then using strlen(str) in the send? I guess not... (As long as we're always null terminated in the correct place.) +int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int except) { char *sendertype; + appendcrlf(str); // Do this just before sending so callers don't need to worry about it + // Decide what sort of text to prefix the debug output with // At the moment if non-zero "except" is specified then it must be a message from a bouncer client // and if "except" is zero then it must be a message from the real IRC server @@ -63,9 +112,9 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, // ...but only if they are connected for (int j = 0; j < MAXCLIENTS; j++) { if (arr_clients[j] == i) { - printf("%s: sending %s to client with fd %d.\n", sendertype, str, i); - if (send(i, str, str_len, 0) == -1) { - perror("send"); + printf("sendtoallclients(): %s: sending %s to client with fd %d.\n", sendertype, str, i); + if (send(i, str, strlen(str), 0) == -1) { + perror("error: sendtoallclients() send()\n"); } } } @@ -80,16 +129,140 @@ int sendtoserver(int *serversockfd, char *str, int str_len) { str_len = strlen(str); // Recalculate str_len in case it changed (TODO: so do we even need to pass it to this function?) printf("sendtoserver(): sending %s to IRC server (length %d).\n", str, str_len); if (send(*serversockfd, str, str_len, 0) == -1) { // 0 is bitwise OR for no send() flags - printf("send error, exiting...\n"); - perror("send"); + printf("error: sendtoserver() send()\n"); + } + + return 0; +} + +int createchannel(struct channel *channels, char *name, char *topic, char *topicwho, char *topicwhen, char *modes, char *namestype) { + printf("createchannel(): given \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", and \"%s\".\n", name, topic, topicwho, topicwhen, modes, namestype); + + + // Find a free slot in the array (with the channel name is not set (0th character is '\0')... + for (int i = 0; i < MAXCHANNELS; i++) { + if (!channels[i].name[0]) { + // ...and set the name and topic + strncpy(channels[i].name, name, strlen(name)); + strncpy(channels[i].topic, topic, strlen(topic)); + strncpy(channels[i].topicwho, topicwho, strlen(topicwho)); + strncpy(channels[i].topicwhen, topicwhen, strlen(topicwhen)); + strncpy(channels[i].modes, modes, strlen(modes)); + strncpy(channels[i].namestype, topic, strlen(namestype)); + return 1; + break; + } + } + + // TODO - Make a failed return do something to callers + return 0; +} + +int addusertochannel(struct channel *channels, char *channelname, char *nick) { + printf("addusertochannel(): given \"%s\" and \"%s\".\n", channelname, nick); + + for (int i = 0; i < MAXCHANNELS; i++) { + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + // Looking for free user slot... + for (int j = 0; j < MAXCHANUSERS; j++) { + if (!channels[i].nicks[j][0]) { + // ...and adding user + strncpy(channels[i].nicks[j], nick, strlen(nick)); + return 1; + } + } + break; + } + } + + // TODO - Make a failed return do something to callers + return 0; +} + +int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when) { + printf("setchanneltopicwhotime(): given \"%s\", \"%s\", and \"%s\".\n", channelname, who, when); + + for (int i = 0; i < MAXCHANNELS; i++) { + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + strncpy(channels[i].topicwho, who, strlen(who)); + strncpy(channels[i].topicwhen, when, strlen(when)); + return 1; + } + } + + // TODO - Make a failed return do something to callers + return 0; +} + +int setchanneltopic(struct channel *channels, char *channelname, char *topic) { + printf("setchanneltopic(): given \"%s\" and \"%s\".\n", channelname, topic); + + for (int i = 0; i < MAXCHANNELS; i++) { + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + strncpy(channels[i].topic, topic, strlen(topic)); + return 1; + } + } + + // TODO - Make a failed return do something to callers + return 0; +} + +int setchannelnamestype(struct channel *channels, char *channelname, char *type) { + printf("setchannelnamestype(): given \"%s\" and \"%s\".\n", channelname, type); + + for (int i = 0; i < MAXCHANNELS; i++) { + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + strncpy(channels[i].namestype, type, strlen(type)); + return 1; + } } + // TODO - Make a failed return do something to callers return 0; } +int getchannelcount(struct channel *channels) { + int count = 0; + + for (int i = 0; i < MAXCHANNELS; i++) { + if (channels[i].name[0]) { + count++; + } + } + + printf("getchannelcount(): counted %d channels.\n", count); + + return count; +} + +int getchannelnamescount(struct channel *channels, char *channelname) { + int count = 0; + + // Find channel + for (int i = 0; i < MAXCHANNELS; i++) { + if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { + // Count nicks + for (int j = 0; j < MAXCHANUSERS; j++) { + // If not null character then it's a nick + if (channels[i].nicks[j][0]) { + count++; + } + } + break; + } + } + + return count; + printf("getchannelnamescount(): counted %d channels for channel '%s'.\n", count, channelname); +} + // Figure out what to do with each CRLF-split IRC message (if anything) // by splitting out the different components by space character (ASCII 0x20). -// Get serversockfd and clientsockfd in case we need to send a response. +// +// serversockfd, clientsockfd, fdmax, arr_clients, except all passed to here so we can +// send/relay a response to server or (other) client(s) directly from this function if +// we want to. // // str is the raw string // @@ -97,7 +270,8 @@ int sendtoserver(int *serversockfd, char *str, int str_len) { // // Return 1 if we processed something and expect the caller to not need to do anything more // Return 0 if we didn't process it and the caller might want to do something -int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source) { +//int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source) { +int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels) { // Track which space-separated token within this response we're on int counter = 0; @@ -106,9 +280,12 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc printf(" >> processircmessage(): Processing source %d message \"%s\"...\n", source, str); + // Copy to a temporary string so we still have the original in case it's not processed + char *strcopy = strdup(str); + char *token; - while ((token = strsep(&str, " ")) != NULL) { + while ((token = strsep(&strcopy, " ")) != NULL) { if (*token == '\0') continue; // Skip consecutive matches printf(" >> Message Token: \"%s\", length %zd.\n", token, strlen(token)); // Copy into the token array (strlen + 1 to get the NULL terminator) @@ -121,9 +298,9 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc switch(source) { case SOURCE_SERVER: // If message(s) were from the real IRC server - // PING received? If so, send a PONG back with the next element as the argument. + // 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) { - printf("PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0])); + printf("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 @@ -135,8 +312,215 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // 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] == ':') { + printf("Prefix found: '%s'! Next token is '%s', length %zd.\n", tokens[0], tokens[1], strlen(tokens[1])); + // Greetings 001 through to 004, 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) { + printf("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'; + printf("Storing our nick!user@host (%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]); + strncpy(ircdstrings->nickuserhost, tokens[counter - 1], strlen(tokens[counter - 1])); + // Null the end of the string + ircdstrings->nickuserhost[strlen(tokens[counter - 1])] = '\0'; + return 1; + } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) { + printf("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) { + printf("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) { + printf("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'; + printf("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'; + return 1; + } + + // Server JOIN received? Add to our local channel list. + if (strncmp(tokens[1], "JOIN", strlen(tokens[1])) == 0) { + printf("Server JOIN found and it is: %s with length %zd! Next token is '%s'. Adding to local channel list.\n", tokens[0], strlen(tokens[0]), tokens[2]); + // Next token should be the channel name but it probably needs the leading ':' stripping + printf("processircmessage(): Channel name was '%s'\n", tokens[2]); + stripprefix(tokens[2]); + printf("processircmessage(): Channel name now '%s'\n", tokens[2]); + + createchannel(channels, tokens[2], "TOPIC", "TOPICWHO", "0", "CHANNELMODES", "="); // TODO - Saner way to initialise this since we don't have the variables yet? + // - Defaulting to type '=' which is "public" since I don't know what else to guess. + + // We processed something so return true + return 1; + } + + // Channel topics/names/nicks/etc. + // Server 332 (RPL_TOPIC) set the channel topic + if (strncmp(tokens[1], "332", strlen(tokens[1])) == 0) { + printf("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); + // 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) { + printf("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) add channel names type (secret/private/public) and the names of those already in the channel + } else if (strncmp(tokens[1], "353", strlen(tokens[1])) == 0) { + // TODO - Do we also need to watch out for 366 (RPL_ENDOFNAMES)? + printf("Server 353 (RPL_NAMREPLY) found, extracting nicks and storing in channel struct.\n"); + // Set channel type + setchannelnamestype(channels, tokens[4], tokens[3]); + // Add nicks + // Strip prefix from first nick first + stripprefix(tokens[5]); + // Now go through all nicks and add them to the channel + for (int i = 5; i < counter; i++) { + addusertochannel(channels, tokens[4], tokens[i]); + } + } + // Don't return if we got here because this means we didn't process something above + } + break; case SOURCE_CLIENT: // If message(s) were from a real IRC client + // USER received? If so, assume this is a new client connecting and catch them on up on the state + if (strncmp(tokens[0], "USER", strlen(tokens[0])) == 0) { + // Need to sort out channel structure first!! + printf("TODO: USER received!\n"); + + // Somewhere to store the several strings we will need to build and send + char outgoingmsg[MAXDATASIZE]; // String to send to client + + // Send IRC greeting strings (001/RPL_WELCOME, 002/RPL_YOURHOST, 003/RPL_CREATED, 004/RPL_MYINFO) to client + snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting001); + sendtoclient(sourcefd, outgoingmsg); + snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting002); + sendtoclient(sourcefd, outgoingmsg); + snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting003); + sendtoclient(sourcefd, outgoingmsg); + snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004); + sendtoclient(sourcefd, outgoingmsg); + + // Get client to join channels, and tell client about those channels + for (int i = 0; i < getchannelcount(channels); i++) { + // 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"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg); + + // Send topic to client - TODO: Handle not having a topic set (331) + if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 332 %s %s :%s", ircdstrings->ircdname, IRCNICK, channels[i].name, channels[i].topic)) { + fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg); + + // Send list of names + // Store count of names so we can increment it in the loop if we encounter gaps in the names array + int namescount = getchannelnamescount(channels, channels[i].name); + // Go through each name... + for (int j = 0; j < namescount; j++) { + // TODO - Batch these up and send multiple names per line + // ...making sure it they are not null (probably a gap in the names array) + if (!channels[i].nicks[j][0]) { + // Skip this one and increment namescount so we find it later on instead + namescount++; + continue; + } + // If there was a nick found, send it to the client (one per line at the moment - TODO - batch them up into fewer lines) + if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 353 %s %s %s :%s", ircdstrings->ircdname, IRCNICK, channels[i].namestype, channels[i].name, channels[i].nicks[j])) { + fprintf(stderr, "Error while preparing USER just connected, channel NAMES responses!\n"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg); + } + + // Once all names are sent, send the "end of /NAMES" 366 (RPL_ENDOFNAMES) message + if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 366 %s %s :End of /NAMES list.", ircdstrings->ircdname, IRCNICK, channels[i].name)) { + fprintf(stderr, "Error while preparing USER just connected, end of NAMES response!\n"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg); + } + + return 1; + } + + // Client PING received? If so, send a PONG back with the next element as the argument. + if (strncmp(tokens[0], "PING", strlen(tokens[0])) == 0) { + printf("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"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg); + + // 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 (strncmp(tokens[0], "CAP", strlen(tokens[0])) == 0) { + printf("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 (strncmp(tokens[0], "NICK", strlen(tokens[0])) == 0) { + printf("Client NICK found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); + sendtoserver(serversockfd, str, strlen(str)); + return 1; + } + + // If PRIVMSG received, send to server, but also reformat and send to all other clients + if (strncmp(tokens[0], "PRIVMSG", strlen(tokens[0])) == 0) { + printf("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(serversockfd, str, strlen(str)); + + // Rebuild to full PRIVMSG string and relay to all other clients + char outgoingmsg[MAXDATASIZE]; // String to send to client + + snprintf(outgoingmsg, MAXDATASIZE, ":%s!%s@%s %s", IRCNICK, IRCUSER, IRCUSERHOST, str); + // Send to all except source client + sendtoallclients(clientsockfd, fdmax, arr_clients, outgoingmsg, sourcefd); + + return 1; + } + + // Just send JOIN to server and let it talk back to clients as required + if (strncmp(tokens[0], "JOIN", strlen(tokens[0])) == 0) { + printf("Client JOIN found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); + sendtoserver(serversockfd, str, strlen(str)); + return 1; + } + + // Don't do anything with QUIT, just let the client go - TODO: Let another clients know with a NOTICE or something + if (strncmp(tokens[0], "QUIT", strlen(tokens[0])) == 0) { + printf("Client QUITfound and it is: %s with length %zd! Doing nothing at all.\n", tokens[0], strlen(tokens[0])); + return 1; + } break; default: fprintf(stderr, "Unexpected raw IRC string source!\n"); @@ -166,7 +550,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // // Return 0 if something went wrong // Return 1 if everything OK -int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int except) { +int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels) { // Copy to a temporary string so we still have the original in case it's not processed char *strcopy = strdup(str); @@ -195,7 +579,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source for (int i = 0; i < messagecount; i++) { // Copy to a temporary string so we still have the original in case it's not processed char *messagecopy = strdup(messages[i]); - if (processircmessage(serversockfd, clientsockfd, messagecopy, source)) { + if (processircmessage(serversockfd, clientsockfd, messagecopy, source, fdmax, arr_clients, sourcefd, ircdstrings, channels)) { printf("Message processed: \"%s\", NULLing...\n", messages[i]); messages[i][0] = '\0'; } @@ -211,15 +595,15 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source // Relay/send to all clients ("except" = 0 because this should send to all clients) // TODO - Is this really going to send the original string if we have messed it with it in processrawstring() and friends!? printf("bouncer-server: sending \"%s\" to all clients, length %zd.\n", messages[i], strlen(messages[i])); - sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], strlen(messages[i]), EXCEPT_NONE); + sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], EXCEPT_NONE); break; case SOURCE_CLIENT: // If message(s) were from a real IRC client // Send to server printf("bouncer-client: sending \"%s\" to the server, length %zd.\n", messages[i], strlen(messages[i])); sendtoserver(serversockfd, messages[i], strlen(messages[i])); - // send the same thing to all *other* clients (all except for fd "i") - sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], strlen(messages[i]), except); + // send the same thing to all *other* clients (all except for source fd) + sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd); break; default: fprintf(stderr, "Unexpected raw IRC string source!\n"); @@ -271,10 +655,31 @@ void dochat(int *serversockfd, int *clientsockfd) { sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg)); // Send our USER - snprintf(outgoingmsg, MAXDATASIZE, "USER %s 8 * : %s", IRCUSER, IRCREALNAME); // TODO - Check for success (with return code) // TODO - Send a more intelligent/correct USER string sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg)); + + // Struct of channels we're in + struct channel *channels; + channels = malloc(sizeof(struct channel) * MAXCHANNELS); + + // Struct of various strings from the real IRCd (such as the greeting strings, the real IRCd's name, our nick!user@host string, etc.) + struct ircdstrings ircdstrings; + // Set them to zero-length strings for now + ircdstrings.greeting001[0] = '\0'; + ircdstrings.greeting002[0] = '\0'; + ircdstrings.greeting003[0] = '\0'; + ircdstrings.greeting004[0] = '\0'; + ircdstrings.ircdname[0] = '\0'; + ircdstrings.nickuserhost[0] = '\0'; + +/* + createchannel(channels, "testchannel", "Test topic!", "+"); + addusertochannel(channels, "testchannel", "l_bratch"); + createchannel(channels, "secondchan", "Yo yo yo", "+nrst"); + addusertochannel(channels, "secondchan", "l_bratch"); + addusertochannel(channels, "secondchan", "coold00d"); +*/ // =============================================> while (1) { @@ -320,11 +725,11 @@ void dochat(int *serversockfd, int *clientsockfd) { } serverbuf[servernumbytes] = '\0'; - printf("BOUNCER-SERVER RECEIVED: %s\n", serverbuf); + printf("BOUNCER-SERVER RECEIVED: '%s'\n", serverbuf); // Try to process received string (which should contain one or more server responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(serversockfd, clientsockfd, serverbuf, SOURCE_SERVER, fdmax, arr_clients, EXCEPT_NONE)) { + if (!processrawstring(serversockfd, clientsockfd, serverbuf, SOURCE_SERVER, fdmax, arr_clients, EXCEPT_NONE, &ircdstrings, channels)) { fprintf(stderr, "Error: bouncer-server failed to process raw string.\n"); exit(1); } @@ -422,7 +827,7 @@ void dochat(int *serversockfd, int *clientsockfd) { // Try to process received string (which should contain one or more client responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(serversockfd, clientsockfd, clientbuf, SOURCE_CLIENT, fdmax, arr_clients, i)) { + if (!processrawstring(serversockfd, clientsockfd, clientbuf, SOURCE_CLIENT, fdmax, arr_clients, i, &ircdstrings, channels)) { fprintf(stderr, "Error: bouncer-client failed to process raw string.\n"); exit(1); } diff --git a/functions.c b/functions.c index 97f8629..86a1f03 100644 --- a/functions.c +++ b/functions.c @@ -50,3 +50,80 @@ void appendcrlf(char *string) { string[startlen + 1] = '\n'; // Add LF string[startlen + 2] = '\0'; // Finish with null terminator } + +// Remove leading colon ':' which is the starting character of a prefix in an IRC message +void stripprefix(char *string) { + // Make a copy to work with + char string2[strlen(string)]; + + printf("stripprefix(): starting with '%s', strlen: %zd.\n", string, strlen(string)); + + // Don't bother if this isn't a prefix with a leading ':' + if (string[0] != ':') { + printf("stripprefix(): no leading ':', returning.\n"); + return; + } + + // Copy the old string into a new one, but... + for (size_t i = 1; i < strlen(string); i++) { + printf("i: %zd. strlen: %zd.\n", i, strlen(string)); + string2[i - 1] = string[i]; + } + + // Copy result back to original string + strncpy(string, string2, strlen(string) - 1); + + // Finish with null terminator + string[strlen(string) - 1] = '\0'; + + printf("stripprefix(): finishing with '%s', strlen: %zd.\n", string, strlen(string)); +} + +// Extract final parameter from IRC message, removing the leading colon ':' +// e.g. given this string: +// ":irc.tghost.co.uk 332 blabounce #test :This is a test topic!" +// We want to end up with: +// "This is a test topic!" +void extractfinalparameter(char *string) { + // Make a copy to work with + char string2[strlen(string)]; + + // Position of colon + int colonpos = -1; + + printf("extractfinalparameter(): starting with '%s', strlen: %zd.\n", string, strlen(string)); + + // Strip the colon at position 0 if there is one + stripprefix(string); + + // Find the colon + for (size_t i = 0; i < strlen(string); i++) { + printf("i: %zd. strlen: %zd.\n", i, strlen(string)); + if (string[i] == ':') { + printf("Found colon at position %zd!\n", i); + colonpos = i; + break; + } + } + + if (colonpos == -1) { + printf("no colon found, returning\n"); + return; + } + + // Build a new string starting from the next position after the colon + int counter = 0; + for (size_t i = colonpos + 1; i < strlen(string); i++) { + printf("i: %zd. strlen: %zd.\n", i, strlen(string)); + string2[counter] = string[i]; + counter++; + } + + // Copy result back to original string + strncpy(string, string2, counter); + + // Finish with null terminator + string[counter] = '\0'; + + printf("extractfinalparameter(): finishing with '%s', strlen: %zd.\n", string, strlen(string)); +} diff --git a/functions.h b/functions.h index 4605da6..d51d61c 100644 --- a/functions.h +++ b/functions.h @@ -27,4 +27,10 @@ int getstdin(char *prompt, char *buff, size_t sz); // Append CR-LF to the end of a string (after cleaning up any existing trailing CR or LF) void appendcrlf(char *string); +// Remove leading colon ':' which is the starting character of a prefix in an IRC message +void stripprefix(char *string); + +// Extract final parameter from IRC message, removing the leading colon ':' +void extractfinalparameter(char *string); + #endif |