summaryrefslogtreecommitdiff
path: root/blabouncer.c
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-05-08 21:02:26 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-05-08 21:02:26 +0100
commit4c9ef597e5503fa571276fd16739410cd9847f91 (patch)
treeca8efa3593a107edd394221be06586fd2726b2ef /blabouncer.c
parent243d947c43a4f71c2a027e9909a154ab48c09907 (diff)
Make TOPIC tracking/following/setting/etc. work for most/all scenarios and ensure it's always given out to new clients correctly. Also misc other bug fixes.
Diffstat (limited to 'blabouncer.c')
-rw-r--r--blabouncer.c196
1 files changed, 157 insertions, 39 deletions
diff --git a/blabouncer.c b/blabouncer.c
index d3ba587..4127b6a 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
+#include <time.h>
#include "functions.h"
#include "sockets.h"
@@ -52,7 +53,9 @@ struct channel {
char name[MAXCHANLENGTH];
char topic[MAXDATASIZE]; // TODO - Is there a particular maximum topic length?
char topicwho[MAXNICKLENGTH];
- char topicwhen[11]; // 32-bit unixtime is up to 10 characters (+1 for null char) // TODO - Make this Year 2038 proof
+ // TODO - Make this Year 2038 proof
+ // TODO - Make this an int? It's just going to arrive and leave as a string every time anyway...
+ char topicwhen[11]; // 32-bit unixtime is up to 10 characters (+1 for null char) We use "0" to mean "not set".
char modes[MAXDATASIZE]; // TODO - Is there a particular maximum modes length?
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?
@@ -152,38 +155,38 @@ int sendtoserver(int *serversockfd, char *str, int str_len) {
return 0;
}
-int removechannel(struct channel *channels, char *name) {
- printf("removechannel(): given \"%s\".\n", name);
+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 the channel in the channel array...
+ // Make sure the channel doesn't already exist
for (int i = 0; i < MAXCHANNELS; i++) {
if (strncmp(channels[i].name, name, strlen(name)) == 0) {
- // ..and NULL its name (0th character = '\0')
- channels[i].name[0] = '\0';
- printf("removechannel(): channel removed.\n");
- return 1;
+ perror("error: createchannel(): channel name already exists.\n");
+ return 0;
+ break;
}
}
- perror("error: removechannel() didn't remove a channel\n");
-
- // TODO - Make a failed return do something to callers
- 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 (when the channel name is not set (0th character is '\0'))...
for (int i = 0; i < MAXCHANNELS; i++) {
if (!channels[i].name[0]) {
// ...and set the name and topic
strncpy(channels[i].name, name, strlen(name));
+ channels[i].name[strlen(name)] = '\0';
+ printf("createchannel(): name given was '%s', length '%ld'.\n", name, strlen(name));
+ printf("createchannel(): name set to '%s', length '%ld'.\n", channels[i].name, strlen(channels[i].name));
strncpy(channels[i].topic, topic, strlen(topic));
+ channels[i].topic[strlen(topic)] = '\0';
+ printf("createchannel(): topic given was '%s', length '%ld'.\n", topic, strlen(topic));
+ printf("createchannel(): topic set to '%s', length '%ld'.\n", channels[i].topic, strlen(channels[i].topic));
strncpy(channels[i].topicwho, topicwho, strlen(topicwho));
+ channels[i].topicwho[strlen(topicwho)] = '\0';
strncpy(channels[i].topicwhen, topicwhen, strlen(topicwhen));
+ channels[i].topicwhen[strlen(topicwhen)] = '\0';
strncpy(channels[i].modes, modes, strlen(modes));
+ channels[i].modes[strlen(modes)] = '\0';
strncpy(channels[i].namestype, topic, strlen(namestype));
+ channels[i].namestype[strlen(namestype)] = '\0';
return 1;
break; // TODO - This should be safe to remove since return is hit first
}
@@ -205,6 +208,7 @@ int addusertochannel(struct channel *channels, char *channelname, char *nick) {
if (!channels[i].nicks[j][0]) {
// ...and adding user
strncpy(channels[i].nicks[j], nick, strlen(nick));
+ channels[i].nicks[j][strlen(nick)] = '\0';
return 1;
}
}
@@ -219,10 +223,15 @@ int addusertochannel(struct channel *channels, char *channelname, char *nick) {
int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when) {
printf("setchanneltopicwhotime(): given \"%s\", \"%s\", and \"%s\".\n", channelname, who, when);
+ printf("setchanneltopicwhotime(): who: '%s' with length '%ld'.\n", who, strlen(who));
+ printf("setchanneltopicwhotime(): when: '%s' with length '%ld'.\n", when, strlen(when));
+
for (int i = 0; i < MAXCHANNELS; i++) {
if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
strncpy(channels[i].topicwho, who, strlen(who));
+ channels[i].topicwho[strlen(who)] = '\0';
strncpy(channels[i].topicwhen, when, strlen(when));
+ channels[i].topicwhen[strlen(when)] = '\0';
return 1;
}
}
@@ -237,6 +246,7 @@ int setchanneltopic(struct channel *channels, char *channelname, char *topic) {
for (int i = 0; i < MAXCHANNELS; i++) {
if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
strncpy(channels[i].topic, topic, strlen(topic));
+ channels[i].topic[strlen(topic)] = '\0';
return 1;
}
}
@@ -251,6 +261,7 @@ int setchannelnamestype(struct channel *channels, char *channelname, char *type)
for (int i = 0; i < MAXCHANNELS; i++) {
if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
strncpy(channels[i].namestype, type, strlen(type));
+ channels[i].namestype[strlen(type)] = '\0';
return 1;
}
}
@@ -288,10 +299,32 @@ int getchannelnamescount(struct channel *channels, char *channelname) {
}
break;
}
- }
+ }
+ printf("getchannelnamescount(): counted %d names for channel '%s'.\n", count, channelname);
return count;
- printf("getchannelnamescount(): counted %d channels for channel '%s'.\n", count, channelname);
+}
+
+int removechannel(struct channel *channels, char *name) {
+ printf("removechannel(): given \"%s\".\n", name);
+
+ // Clear its topic setter and timestamp...
+ setchanneltopicwhotime(channels, name, "", "0");
+
+ // Find the channel in the channel array...
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, name, strlen(name)) == 0) {
+ // ..and NULL its name (0th character = '\0')
+ channels[i].name[0] = '\0';
+ printf("removechannel(): channel removed and topicwhen set to '%s'.\n", channels[i].topicwhen);
+ return 1;
+ }
+ }
+
+ perror("error: removechannel() didn't remove a channel\n");
+
+ // TODO - Make a failed return do something to callers
+ return 0;
}
// Figure out what to do with each CRLF-split IRC message (if anything)
@@ -390,16 +423,27 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
return 1;
}
- // Server JOIN received? Add to our local channel list.
+ // Server JOIN received? Add to our local channel array if it's us.
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]);
+ printf("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
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.
+ // If the user JOINing is us, then we must have joined a channel, so add to our local channel array.
+ // (If it's not us, then it's another user JOINing 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]);
+ stripprefix(prefixcopy);
+ if (strncmp(prefixcopy, ircdstrings->nickuserhost, strlen(tokens[0])) == 0) {
+ printf("Server JOIN: nickuserhost is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost);
+ // 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", "CHANNELMODES", "=");
+ } else {
+ printf("Server JOIN: nickuserhost is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost);
+ }
// And then send to all clients
sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd);
@@ -417,8 +461,14 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
}
// 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
- if (strncmp(tokens[1], "332", strlen(tokens[1])) == 0) {
+ } else 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
@@ -444,6 +494,39 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
}
}
+ // Server TOPIC received? Update our local channel topic info then relay to clients
+ if (strncmp(tokens[1], "TOPIC", strlen(tokens[1])) == 0) {
+ printf("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(clientsockfd, fdmax, arr_clients, str, sourcefd);
+ return 1;
+ }
+
// Don't return if we got here because this means we didn't process something above
}
@@ -467,8 +550,20 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004);
sendtoclient(sourcefd, outgoingmsg);
+ // Get the channel count so we can enumerate over all channels.
+ // Storing separately so we can skip over blank channels.
+ int channelcount = getchannelcount(channels);
+
// Get client to join channels, and tell client about those channels
- for (int i = 0; i < getchannelcount(channels); i++) {
+ for (int i = 0; i < channelcount; i++) {
+ printf("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]) {
+ printf("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");
@@ -476,12 +571,34 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
}
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);
+ // 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, IRCNICK, channels[i].name)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 331 RPL_NOTOPIC!\n");
+ exit(1);
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg);
+ // 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, IRCNICK, channels[i].name, channels[i].topic)) {
+ fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses, 332 RPL_TOPIC!\n");
+ exit(1);
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg);
+
+ // Next prepare the topic who/when message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 333 %s %s %s %s", ircdstrings->ircdname, 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");
+ exit(1);
+ }
+ // ..and send it to the client
+ sendtoclient(sourcefd, outgoingmsg);
}
- 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
@@ -581,6 +698,13 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
return 1;
}
+ // Just send TOPIC to server and let it talk back to clients as required
+ if (strncmp(tokens[0], "TOPIC", strlen(tokens[0])) == 0) {
+ printf("Client TOPIC found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0]));
+ sendtoserver(serversockfd, str, strlen(str));
+ return 1;
+ }
+
break;
default:
fprintf(stderr, "Unexpected raw IRC string source!\n");
@@ -654,14 +778,15 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source
case SOURCE_SERVER: // If message(s) were from the real IRC server
// 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 unprocessed messasge \"%s\" to all clients, length %zd.\n", messages[i], strlen(messages[i]));
+ printf("bouncer-server: sending unprocessed server messasge \"%s\" to all clients, length %zd.\n", messages[i], strlen(messages[i]));
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 unprocessed messasge \"%s\" to the server, length %zd.\n", messages[i], strlen(messages[i]));
+ printf("bouncer-client: sending unprocessed client messasge \"%s\" to the server, length %zd.\n", messages[i], strlen(messages[i]));
sendtoserver(serversockfd, messages[i], strlen(messages[i]));
+ printf("bouncer-client: sending unprocessed client messasge \"%s\" to all other clients, length %zd.\n", messages[i], strlen(messages[i]));
// send the same thing to all *other* clients (all except for source fd)
sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd);
break;
@@ -733,13 +858,6 @@ void dochat(int *serversockfd, int *clientsockfd) {
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) {