summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-06-11 22:54:50 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-06-11 22:54:50 +0100
commit511e258e901e5248e1706609ba1099507fd750ae (patch)
tree4c7ee92fbe3634e7c78579657a023948a1a6bbae
parent9db9fb396aaf601bd00f2b62face2693307a0e16 (diff)
Implement configurable auto replay modes. So far there is "none", "time" (auto replay the last X seconds), and "lastspoke" (auto replay everything since you last spoke).
-rw-r--r--Makefile4
-rw-r--r--TODO2
-rw-r--r--blabouncer.c11
-rw-r--r--blabouncer.conf.example8
-rw-r--r--config.c8
-rw-r--r--functions.c44
-rw-r--r--functions.h9
-rw-r--r--message.c4
-rw-r--r--replay.c89
-rw-r--r--replay.h5
-rw-r--r--structures.h1
11 files changed, 170 insertions, 15 deletions
diff --git a/Makefile b/Makefile
index caa293d..28c469f 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CC=gcc
DEPS = functions.h sockets.h config.h replay.h logging.h structures.h message.h
%.o: %.c $(DEPS)
- $(CC) -Wall -Wextra -c -o $@ $<
+ $(CC) -D_DEFAULT_SOURCE -Wall -Wextra -c -o $@ $<
blabouncer: blabouncer.o functions.o sockets.o config.o replay.o logging.o message.o
- $(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o logging.o message.o
+ $(CC) -D_DEFAULT_SOURCE -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o logging.o message.o
diff --git a/TODO b/TODO
index 618b2b9..b49d7bd 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,7 @@
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
+ - 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)
diff --git a/blabouncer.c b/blabouncer.c
index 469eb68..0db2dca 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -789,6 +789,17 @@ int main(int argc, char *argv[]) {
// Populate settings from configuration file
+ // What is the auto replay mode?
+ if (!getconfstr("replaymode", settings.conffile, settings.replaymode)) {
+ 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");
+ exit(1);
+ }
+ }
+
// How many seconds of replay log should automatically be replayed - TODO - Can we do error checking on this?
settings.replayseconds = getconfint("replayseconds", settings.conffile);
if (errno == ECONFINT) {
diff --git a/blabouncer.conf.example b/blabouncer.conf.example
index f4de92e..f9c09bc 100644
--- a/blabouncer.conf.example
+++ b/blabouncer.conf.example
@@ -18,7 +18,13 @@ realname = "Mr Bla Bouncer"
# Put channel keywords/passwords after channel names following a space.
#channels = "#blabouncer keyword,#test"
-# How many seconds of replay log should be sent to connecting clients
+# Auto replay mode upon a bouncer client connecting
+# "none" = Don't auto replay
+# "time" = Always send the last "replayseconds" worth of logs
+# "lastspoke" = All messages since your current nick last spoke
+replaymode = "time"
+
+# How many seconds of replay log should be sent to connecting clients if replaymode = "time"
replayseconds = "600"
# Connect password clients must provided to connect
diff --git a/config.c b/config.c
index dd99f2a..94831d1 100644
--- a/config.c
+++ b/config.c
@@ -202,7 +202,13 @@ int createconfigfile(char *filename) {
"# Put channel keywords/passwords after channel names following a space.\n"
"#channels = \"#blabouncer keyword,#test\"\n"
"\n"
- "# How many seconds of replay log should be sent to connecting clients\n"
+ "# Auto replay mode upon a bouncer client connecting\n"
+ "# \"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"
+ "replaymode = \"time\"\n"
+ "\n"
+ "# How many seconds of replay log should be sent to connecting clients if replaymode = \"time\"\n"
"replayseconds = \"600\"\n"
"\n"
"# Connect password clients must provided to connect\n"
diff --git a/functions.c b/functions.c
index 93fd52b..31dce90 100644
--- a/functions.c
+++ b/functions.c
@@ -734,9 +734,9 @@ int channelindex(struct channel *channels, char *name) {
return -1;
}
-// Send the requested number of lines of replay log to the requested client
+// Send the requested number of lines of replay log to the requested client.
// 'sourcefd' is the client to send to, and replayseconds is the number of
-// seconds of replay to replay.
+// seconds of replay log to replay.
// Returns 1 for success or 0 for failure.
int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels) {
char outgoingmsg[MAXDATASIZE];
@@ -822,6 +822,46 @@ int doreplay(int sourcefd, int replayseconds, struct client *clients, struct set
return 1;
}
+// Send the auto replay to the requested client, where 'sourcefd' is the client
+// to send to. The type of replay will depend on the user's settings.
+// Returns 1 for success or 0 for failure.
+int doautoreplay(int sourcefd, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels) {
+ // If replaymode = "none" then don't send anything
+ if (!strncmp(settings->replaymode, "none", strlen("none"))) {
+ debugprint(DEBUG_FULL, "doautoreplay() requested but replaymode = \"none\".\n");
+ return 1;
+ }
+
+ // If replaymode = "time" then send whatever replayseconds is set to
+ if (!strncmp(settings->replaymode, "time", strlen("time"))) {
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"time\", calling doreplay() with '%d' seconds.\n", settings->replayseconds);
+ if (!doreplay(sourcefd, settings->replayseconds, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplay() returned 0, returning 0 to caller...\n");
+ return 0;
+ }
+ return 1;
+ }
+
+ // If replaymode = "lastspoke" then send whatever replayseconds is set to
+ if (!strncmp(settings->replaymode, "lastspoke", strlen("lastspoke"))) {
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastspoke\".\n", settings->replayseconds);
+ int secondsago = lastspokesecondsago(ircdstate->ircnick, settings->basedir);
+ if (secondsago < 1) {
+ debugprint(DEBUG_SOME, "doautoreplay(): lastspokesecondsago() returned < 1, returning 0 to caller...\n");
+ return 0;
+ }
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastspoke\", sending lines from '%d' seconds ago to sourcefd '%d'.\n", secondsago, sourcefd);
+ if (!doreplay(sourcefd, secondsago, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplay() returned 0, returning 0 to caller...\n");
+ return 0;
+ }
+ return 1;
+ }
+
+ // We shouldn't get here
+ return 0;
+}
+
// Return a count of the number of connected clients
int numclients(struct client *clients) {
int count = 0;
diff --git a/functions.h b/functions.h
index 7638b61..c0e90f8 100644
--- a/functions.h
+++ b/functions.h
@@ -127,12 +127,17 @@ int inchannel(struct channel *channels, char *name);
// Returns -1 if there was an error.
int channelindex(struct channel *channels, char *name);
-// Send the requested number of lines of replay log to the requested client
+// Send the requested number of lines of replay log to the requested client.
// 'sourcefd' is the client to send to, and replayseconds is the number of
-// seconds of replay to replay.
+// seconds of replay log to replay.
// Returns 1 for success or 0 for failure.
int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels);
+// Send the auto replay to the requested client, where 'sourcefd' is the client
+// to send to. The type of replay will depend on the user's settings.
+// Returns 1 for success or 0 for failure.
+int doautoreplay(int sourcefd, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels);
+
// Return a count of the number of connected clients
int numclients(struct client *clients);
diff --git a/message.c b/message.c
index e8879b2..97d15c8 100644
--- a/message.c
+++ b/message.c
@@ -126,7 +126,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
for (int i = 0; i < MAXCLIENTS; i++) {
if (clients[i].fd) {
char alertmsg[MAXDATASIZE];
- if (!doreplay(clients[i].fd, settings->replayseconds, clients, settings, ircdstate, channels)) {
+ if (!doautoreplay(clients[i].fd, clients, settings, ircdstate, channels)) {
snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstate->ircnick);
sendtoclient(sourcefd, alertmsg, clients, settings, 0);
}
@@ -947,7 +947,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
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, ircdstate, channels)) {
+ if (!doautoreplay(sourcefd, clients, settings, ircdstate, channels)) {
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
}
diff --git a/replay.c b/replay.c
index 4850a22..3a40e16 100644
--- a/replay.c
+++ b/replay.c
@@ -188,7 +188,7 @@ int replaylines(int seconds, char *basedir) {
}
// If the line is within range of the requested time, count it
- if (timestamp > timenow - seconds) {
+ if (timestamp >= timenow - seconds) {
numlines++;
}
}
@@ -216,7 +216,7 @@ int readreplayline(int seconds, int linenum, char *str, char *basedir) {
fp = fopen(filename, "r");
if (fp == NULL) {
- debugprint(DEBUG_CRIT, "error: could not open replay log '%s'.\n", filename);
+ debugprint(DEBUG_CRIT, "error: readreplayline(): could not open replay log '%s'.\n", filename);
fclose(fp);
return 0;
}
@@ -228,13 +228,13 @@ int readreplayline(int seconds, int linenum, char *str, char *basedir) {
// Read the timestamp from each line
int timestamp = gettimestamp(line);
if (timestamp < 1) {
- debugprint(DEBUG_CRIT, "Error reading timestamp from replay log file.\n");
+ debugprint(DEBUG_CRIT, "readreplayline(): Error reading timestamp from replay log file.\n");
fclose(fp);
return 0;
}
// If the line is within range of the requested time...
- if (timestamp > timenow - seconds) {
+ if (timestamp >= timenow - seconds) {
// ...and it is the current requested line then return it
if (count == linenum) {
// Insert our formatted [HH:MM:SS] timestamp into the message
@@ -254,6 +254,87 @@ int readreplayline(int seconds, int linenum, char *str, char *basedir) {
return 0;
}
+// Returns the number of seconds ago that 'nick' last spoke, or -1 if there is a problem.
+// 'basedir' is the directory in which to find 'replay.log'.
+int lastspokesecondsago(char *nick, char *basedir) {
+ FILE *fp;
+ char str[MAXCHAR];
+ char filename[PATH_MAX];
+
+ // Build path
+ snprintf(filename, PATH_MAX, "%s/replay.log", basedir);
+
+ int lastspoketime = 0; // When 'nick' last spoke
+
+ // Get the current time for comparison later
+ int timenow = (int)time(NULL);
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ printf("error: replaylineslastspoke(): could not open replay log '%s'.\n", filename);
+ // Assume the file just doesn't exist yet - TODO - Interpret error codes to see what happened.
+ fclose(fp);
+ return 0;
+ }
+
+ while (fgets(str, MAXCHAR, fp) != NULL) {
+ // Split the line up to determine if it was a PRIVMSG sent by the requested 'nick'
+ // TODO - This may also be terribly inefficient
+
+ // Copy to a temporary string
+ char *strcopy = strdup(str);
+ // Keep track of initial pointer for free()ing later
+ char *strcopyPtr = strcopy;
+
+ // Build array of each space-separated token, only need three (<timestamp> :<nick>!<user>@<host> PRIVMSG)
+ char tokens[3][MAXDATASIZE + TIMELEN]; // Make IRC message length + our unix timestamp
+ char *token;
+ int counter = 0;
+ for (int i = 0; i < 3; i++) {
+ // Try to split
+ if ((token = strsep(&strcopy, " ")) == NULL) {
+ debugprint(DEBUG_CRIT, "replaylineslastspoke(): error splitting string on iteration '%d', returning -1!\n", i);
+ return -1;
+ }
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(tokens[i], token, strlen(token) + 1);
+ counter++;
+ }
+ free(strcopyPtr);
+
+ // Make sure there were at least three tokens
+ if (counter < 3) {
+ debugprint(DEBUG_CRIT, "replaylineslastspoke(): not enough tokens on line, only '%d', returning -1!\n", counter);
+ return -1;
+ }
+
+ // Make sure it started with a valid timestamp
+ int timestamp = gettimestamp(tokens[0]);
+ if (timestamp < 0) {
+ debugprint(DEBUG_CRIT, "replaylineslastspoke(): line didn't start with a timestamp, returning -1!\n", counter);
+ }
+
+ // Is it a PRIVMSG?
+ if (strncmp(tokens[2], "PRIVMSG", strlen("PRIVMSG"))) {
+ // Not a PRIVMSG, continue
+ continue;
+ }
+
+ // Was it said by our 'nick'?
+ extractnickfromprefix(tokens[1]);
+ if (strncmp(tokens[1], nick, strlen(nick))) {
+ // Not our 'nick', continue
+ continue;
+ }
+
+ lastspoketime = timestamp;
+ }
+
+ fclose(fp);
+ return timenow - lastspoketime;
+}
+
// Write the line 'str' to the replay log file after prepending it with
// the current unixtime timestamp. 'basedir' is the directory in which
// to write to 'replay.log'.
diff --git a/replay.h b/replay.h
index ee3f483..5ae9994 100644
--- a/replay.h
+++ b/replay.h
@@ -19,6 +19,7 @@
#define REPLAY_H_INCLUDED
#define _XOPEN_SOURCE
+#define _XOPEN_SOURCE_EXTENDED
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -40,6 +41,10 @@ int replaylines(int seconds, char *basedir);
int readreplayline(int seconds, int linenum, char *str, char *basedir);
+// Returns the number of seconds ago that 'nick' last spoke, or -1 if there is a problem.
+// 'basedir' is the directory in which to find 'replay.log'.
+int lastspokesecondsago(char *nick, char *basedir);
+
int writereplayline(char *str, char *basedir);
#endif
diff --git a/structures.h b/structures.h
index ac68ba9..9246fa4 100644
--- a/structures.h
+++ b/structures.h
@@ -52,6 +52,7 @@ struct ircdstate {
// Structure of settings either to be read from the configuration file or set/changed at runtime
struct settings {
+ char replaymode[MAXDATASIZE];
int replayseconds;
char clientport[MAXPORTLEN];
char ircnick[MAXNICKLENGTH]; // In both settings and ircdstate as settings is from our file whereas server may change ircdstate copy