From 61906329ccbe96c25c75533f819dea269492f5a7 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sun, 16 Jun 2019 23:16:18 +0100 Subject: Implement two new auto replay modes: - replaymode = "noclients": All messages since the bouncer last had no clients connected - replaymode = "lastchange": All messages since the last client connect or disconnect --- TODO | 6 ++---- blabouncer.c | 10 +++++++--- blabouncer.conf.example | 2 ++ config.c | 2 ++ functions.c | 33 ++++++++++++++++++++++++++++++--- message.c | 3 +++ structures.h | 2 ++ 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index c5a5517..e58b82c 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,6 @@ Add various auto replay options: - - All logs since the final client disconnected - - All logs since the most recent client connect/disconnect - - All logs since *you* last sent a message (already implemented) - - All logs since X seconds ago (already implemented) - All logs since the current client last disconnected (track clients with some special token the client auto sends on connect) Might need to #include in blabouncer.c to make some operating systems and/or compilers happy. + +Don't say "Unable to read replay log file!" just because there was nothing to replay. diff --git a/blabouncer.c b/blabouncer.c index 3b0a538..95521f1 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -133,6 +133,7 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, ircdstate->lastmessagetime = time(NULL); ircdstate->timeoutcheck = 0; // ircdstate.reconnecting is not set here as we want to track reconnections separately + // ircdstate.clientchangetime and ircdstate.clientsnonetime not set here as they are set at startup and only changed when clients connect/disconnect // Populate nick and username from our configuration file for now, real IRCd may change them later (TODO - Is this true of username?) strcpy(ircdstate->ircnick, settings->ircnick); @@ -412,9 +413,11 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { SSL_CTX *serverctx = NULL; SSL *server_ssl = NULL; // Need to create this either way as referenced later - // Set reconnection things to null/zero for now (not used unless reconnecting to server) + // Set reconnection and other things to null/zero for now (not used unless reconnecting to server) ircdstate.oldnick[0] = '\0'; ircdstate.reconnecting = 0; + ircdstate.clientchangetime = time(NULL); + ircdstate.clientsnonetime = time(NULL); if (!connecttoircserver(&serverctx, &server_ssl, serversockfd, &ircdstate, settings, clients)) { fprintf(stderr, "Failed to connect to IRC server, exiting.\n"); debugprint(DEBUG_CRIT, "Failed to connect to IRC server, exiting.\n"); @@ -877,8 +880,9 @@ int main(int argc, char *argv[]) { printf("main(): error getting 'replaymode' from configuration file.\n"); exit(1); } else { - if (strcmp(settings.replaymode, "none") && strcmp(settings.replaymode, "time") && strcmp(settings.replaymode, "lastspoke")) { - printf("main(): replaymode in configuration file must be one of \"none\", \"time\", or \"lastspoke\".\n"); + if (strcmp(settings.replaymode, "none") && strcmp(settings.replaymode, "time") && strcmp(settings.replaymode, "lastspoke") && + strcmp(settings.replaymode, "noclients") && strcmp(settings.replaymode, "lastchange")) { + printf("main(): replaymode in configuration file must be one of \"none\", \"time\", \"lastspoke\", \"noclients\", or \"lastchange\".\n"); exit(1); } } diff --git a/blabouncer.conf.example b/blabouncer.conf.example index ebb2f23..1537805 100644 --- a/blabouncer.conf.example +++ b/blabouncer.conf.example @@ -24,6 +24,8 @@ realname = "Mr Bla Bouncer" # "none" = Don't auto replay # "time" = Always send the last "replayseconds" worth of logs # "lastspoke" = All messages since your current nick last spoke +# "noclients" = All messages since you last had no clients connected +# "lastchange" = All messages since your last client connection/disconnection replaymode = "time" # How many seconds of replay log should be sent to connecting clients if replaymode = "time" diff --git a/config.c b/config.c index 9a144cd..6246789 100644 --- a/config.c +++ b/config.c @@ -178,6 +178,8 @@ int createconfigfile(char *filename) { "# \"none\" = Don't auto replay\n" "# \"time\" = Always send the last \"replayseconds\" worth of logs\n" "# \"lastspoke\" = All messages since your current nick last spoke\n" + "# \"noclients\" = All messages since you last had no clients connected\n" + "# \"lastchange\" = All messages since your last client connection/disconnection\n" "replaymode = \"time\"\n" "\n" "# How many seconds of replay log should be sent to connecting clients if replaymode = \"time\"\n" diff --git a/functions.c b/functions.c index 3aa474f..edb77e7 100644 --- a/functions.c +++ b/functions.c @@ -550,6 +550,10 @@ int disconnectclient(int fd, struct client *clients, struct ircdstate *ircdstate for (int i = 0; i < MAXCLIENTS; i++) { if (clients[i].fd == fd) { debugprint(DEBUG_FULL, "found and clearing fd %d from clients[%d]\n", fd, i); + // If the client was registered, record the time of the last client disconnect + if (clients[i].registered) { + ircdstate->clientchangetime = time(NULL); + } clients[i].fd = 0; clients[i].authed = 0; clients[i].registered = 0; @@ -569,6 +573,10 @@ int disconnectclient(int fd, struct client *clients, struct ircdstate *ircdstate close(fd); // Now clients array is cleared, inform all other clients (source "0" since we trust this message) sendtoallclients(clients, alertmsg, 0, settings); + // If there are now no clients connected, record the time + if (numclients(clients) == 0) { + ircdstate->clientsnonetime = time(NULL); + } return 1; } } @@ -858,7 +866,7 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings return 1; } - // If replaymode = "lastspoke" then send whatever replayseconds is set to + // If replaymode = "lastspoke" then send whatever happened since the user's current nick last spoke if (!strncmp(settings->replaymode, "lastspoke", strlen("lastspoke"))) { debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastspoke\".\n", settings->replayseconds); int secondsago = lastspokesecondsago(ircdstate->ircnick, settings->basedir); @@ -874,6 +882,24 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings return 1; } + // If replaymode = "noclients" and there is currently only one client connected, then send whatever happened since there were last no clients connected + if (!strncmp(settings->replaymode, "noclients", strlen("noclients")) && numclients(clients) == 1) { + debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"noclients\", sending '%d' seconds of replay.\n", time(NULL) - ircdstate->clientsnonetime); + if (!doreplay(sourcefd, time(NULL) - ircdstate->clientsnonetime, clients, settings, ircdstate, channels)) { + debugprint(DEBUG_SOME, "doautoreplay(): doreplay() returned 0, returning 0 to caller...\n"); + return 0; + } + } + + // If replaymode = "lastchange" then send whatever happened since the last client registration or disconnection + if (!strncmp(settings->replaymode, "lastchange", strlen("lastchange"))) { + debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastchange\", sending '%d' seconds of replay.\n", time(NULL) - ircdstate->clientchangetime); + if (!doreplay(sourcefd, time(NULL) - ircdstate->clientchangetime, clients, settings, ircdstate, channels)) { + debugprint(DEBUG_SOME, "doautoreplay(): doreplay() returned 0, returning 0 to caller...\n"); + return 0; + } + } + // We shouldn't get here return 0; } @@ -1046,9 +1072,10 @@ int rehash(struct settings *settings, char *failuremsg) { strcpy(failuremsg, "error getting 'replaymode' from configuration file"); return 0; } else { - if (strcmp(settings->replaymode, "none") && strcmp(settings->replaymode, "time") && strcmp(settings->replaymode, "lastspoke")) { + if (strcmp(settings->replaymode, "none") && strcmp(settings->replaymode, "time") && strcmp(settings->replaymode, "lastspoke") && + strcmp(settings->replaymode, "noclients") && strcmp(settings->replaymode, "lastchange")) { strcpy(settings->replaymode, oldreplaymode); - strcpy(failuremsg, "replaymode in configuration file must be one of \"none\", \"time\", or \"lastspoke\""); + strcpy(failuremsg, "replaymode in configuration file must be one of \"none\", \"time\", \"lastspoke\", \"noclients\", or \"lastchange\""); return 0; } } diff --git a/message.c b/message.c index 0137fd4..3e79605 100644 --- a/message.c +++ b/message.c @@ -950,6 +950,9 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); } + // And record the time of the last client registration + ircdstate->clientchangetime = time(NULL); + return 1; } diff --git a/structures.h b/structures.h index 3209b21..d1ec4b0 100644 --- a/structures.h +++ b/structures.h @@ -48,6 +48,8 @@ struct ircdstate { int timeoutcheck; // Whether we're checking to see if we've timed out from the server int reconnecting; // Whether or not we're reconnecting due to an earlier disconnection char oldnick[MAXNICKLENGTH]; // Set temporarily if we end up reconnecting in case we need to tell existing clients about a nick change + int clientchangetime; // The last time a client registered or disconnected + int clientsnonetime; // The last time there were no clients registered }; // Structure of settings either to be read from the configuration file or set/changed at runtime -- cgit v1.2.3