summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2022-11-24 00:08:34 +0000
committerLuke Bratch <luke@bratch.co.uk>2022-11-24 00:08:34 +0000
commit2e665e03b6175b3f31f0ef1e058183417df1456e (patch)
tree32674c99ac25a3df5c166db89260c60e15764c10
parent680d8535a87be8aa0d5ef6432d1f87561ebfcb5f (diff)
Fix replaymode = "lastspoke" by using line numbers rather than time to calculate replay start point.
Introduce new function doreplaylastspoke() to achieve this, move doreplay() into replay.c as doreplaytime() and refactor common things into sanitisereplay().
-rw-r--r--TODO3
-rw-r--r--functions.c132
-rw-r--r--functions.h6
-rw-r--r--message.c4
-rw-r--r--replay.c233
-rw-r--r--replay.h18
6 files changed, 246 insertions, 150 deletions
diff --git a/TODO b/TODO
index a5f65f0..e10a4e9 100644
--- a/TODO
+++ b/TODO
@@ -10,6 +10,7 @@ macOS compiler may need limits.h included in structures.h.
Sometimes replaymode = "lastspoke" will replay the last message you sent if you spoke last and sometimes it doesn't - change to always include your last message?
Time based issue due to "seconds ago" rather than specific timestamp? Perhaps extract timestamp and replay from there.
+(Pending real world testing to be marked as fixed.)
Can memory usage be reduced further? (e.g. better channel struct management)
@@ -40,4 +41,6 @@ Git version in code.
Sending ^D when running with ./blabouncer -f does crazy stuff.
+Connecting from irssi triggers doautoreplay() from "USER received" in message.c twice.
+
Crash when requesting 30 hour replay.
diff --git a/functions.c b/functions.c
index 917f688..6d85d8e 100644
--- a/functions.c
+++ b/functions.c
@@ -873,110 +873,6 @@ int channelindex(struct channel *channels, int maxchannelcount, char *name) {
return -1;
}
-// Send the requested number of seconds worth of replay log lines to the requested client.
-// 'sourcefd' is the client to send to, and 'replayseconds' is the number of
-// 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];
-
- // Figure out how many lines to replay
- int numlines = replaylines(replayseconds, settings->basedir);
- debugprint(DEBUG_FULL, "Replay log lines: '%d'.\n", numlines);
-
- if (numlines < 0) {
- debugprint(DEBUG_CRIT, "Error getting number of replay lines.\n");
- return 0;
- } else if (numlines == 0) {
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", ircdstate->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- return 1;
- }
-
- // Announce the start
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Starting log replay....", ircdstate->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- // Replay those lines!
- for (int i = 0; i < numlines; i++) {
- int ret = readreplayline(replayseconds, i, outgoingmsg, settings, ircdstate);
- if (ret == 0) {
- debugprint(DEBUG_CRIT, "Error requesting replay line.\n");
- return 0;
- } else if (ret == -1) {
- debugprint(DEBUG_FULL, "doreplay(): readreplayline() said to ignore replay line.\n");
- continue;
- }
-
- // Check if the replay line is a TOPIC, a JOIN, or a PART so we don't
- // replay those if we are not currently in the channel they are from
- // otherwise clients and state go a bit mad.
- // Never replay them if they are from us.
-
- // Copy to a temporary string
- char *strcopy = strdup(outgoingmsg);
- // Keep track of initial pointer for free()ing later
- char *strcopyPtr = strcopy;
-
- // Build array of each space-separated token
- char tokens[3][MAXDATASIZE];
- char *token;
-
- for (int j = 0; j < 3; j++) {
- // Try to split
- if ((token = strsep(&strcopy, " ")) == NULL) {
- debugprint(DEBUG_CRIT, "doreplay(): error splitting string on iteration '%d', returning!\n", j);
- return 0;
- }
- // Copy into the token array (strlen + 1 to get the NULL terminator)
- strncpy(tokens[j], token, strlen(token) + 1);
- }
-
- if (strncmp(tokens[1], "TOPIC", strlen("TOPIC")) == 0 ||
- strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0 ||
- strncmp(tokens[1], "PART", strlen("PART")) == 0) {
- // Skip over colon if present in channel name
- int offset = 0;
- if (tokens[2][0] == ':') {
- offset = 1;
- }
-
- // To make sure it's not us
- extractnickfromprefix(tokens[0], 1);
-
- // Check if we're currently in this channel or if the log line is from us
- if (!inchannel(channels, ircdstate->maxchannelcount, tokens[2] + offset) ||
- ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0))) {
- debugprint(DEBUG_FULL, "Not sending '%s' replay line '%s'.\n", tokens[1], outgoingmsg);
- free(strcopyPtr);
- continue;
- }
- }
-
- // Separate special check for if a NICK change is from us but it isn't our current nick
- if (strncmp(tokens[1], "NICK", strlen("NICK")) == 0) {
- extractnickfromprefix(tokens[0], 1);
-
- if ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0)) {
- debugprint(DEBUG_FULL, "Not sending '%s' replay line '%s'.\n", tokens[1], outgoingmsg);
- free(strcopyPtr);
- continue;
- }
- }
-
- free(strcopyPtr);
-
- debugprint(DEBUG_FULL, "Sending replay line: '%s'.\n", outgoingmsg);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- }
-
- // Announce the end
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstate->ircnick);
- sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
-
- 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.
@@ -989,9 +885,9 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings
// 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");
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"time\", calling doreplaytime() with '%d' seconds.\n", settings->replayseconds);
+ if (!doreplaytime(sourcefd, settings->replayseconds, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplaytime() returned 0, returning 0 to caller...\n");
return 0;
}
return 1;
@@ -999,15 +895,15 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings
// 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);
- if (secondsago < 1) {
- debugprint(DEBUG_SOME, "doautoreplay(): lastspokesecondsago() returned < 1, returning 0 to caller...\n");
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastspoke\", calling doreplaylastspoke().\n");
+ long linenumber = lastspokelinenumber(ircdstate->ircnick, settings->basedir);
+ if (linenumber < 1) {
+ debugprint(DEBUG_SOME, "doautoreplay(): lastspokelinenumber() 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");
+ debugprint(DEBUG_FULL, "doautoreplay(): replaymode = \"lastspoke\", replaying from line '%ld' to sourcefd '%d'.\n", linenumber, sourcefd);
+ if (!doreplaylastspoke(sourcefd, linenumber, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplaylastspoke() returned 0, returning 0 to caller...\n"); // CORRECT?
return 0;
}
return 1;
@@ -1016,8 +912,8 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings
// 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");
+ if (!doreplaytime(sourcefd, time(NULL) - ircdstate->clientsnonetime, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplaytime() returned 0, returning 0 to caller...\n");
return 0;
}
return 1;
@@ -1026,8 +922,8 @@ int doautoreplay(int sourcefd, struct client *clients, struct settings *settings
// 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");
+ if (!doreplaytime(sourcefd, time(NULL) - ircdstate->clientchangetime, clients, settings, ircdstate, channels)) {
+ debugprint(DEBUG_SOME, "doautoreplay(): doreplaytime() returned 0, returning 0 to caller...\n");
return 0;
}
return 1;
diff --git a/functions.h b/functions.h
index c22d7ac..28b6413 100644
--- a/functions.h
+++ b/functions.h
@@ -142,12 +142,6 @@ int inchannel(struct channel *channels, int maxchannelcount, char *name);
// Returns -1 if there was an error.
int channelindex(struct channel *channels, int maxchannelcount, char *name);
-// 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 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.
diff --git a/message.c b/message.c
index 9c07a83..c205709 100644
--- a/message.c
+++ b/message.c
@@ -1370,7 +1370,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
debugprint(DEBUG_FULL, "Replaying '%s' which is '%d' seconds.\n", tokens[2], replayseconds);
- if (!doreplay(sourcefd, replayseconds, clients, settings, ircdstate, channels)) {
+ if (!doreplaytime(sourcefd, replayseconds, clients, settings, ircdstate, channels)) {
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
}
@@ -1448,7 +1448,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
return 1;
}
- if (!doreplay(sourcefd, time(NULL) - codetime, clients, settings, ircdstate, channels)) {
+ if (!doreplaytime(sourcefd, time(NULL) - codetime, clients, settings, ircdstate, channels)) {
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unable to read replay log file!", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
return 1;
diff --git a/replay.c b/replay.c
index 5dcf5e6..c63e859 100644
--- a/replay.c
+++ b/replay.c
@@ -175,9 +175,78 @@ void formattime(char *str, int replaydates) {
str[len + strlen(timestampf)] = '\0';
}
+// Sanitise a potential replay string 'str' by detecting if replay line is any of:
+// 1. TOPIC/JOIN/PART but we're not in the channel any more
+// 2. TOPIC/JOIN/PART and it was from us
+// 3. NICK from us but not our current nick
+// Returns 1 if the replay line should go through as is
+// Returns 0 if the replay line should be skipped
+// Returns -1 on error
+int sanitisereplay (char *str, struct ircdstate *ircdstate, struct channel *channels) {
+ // Check if the replay line is a TOPIC, a JOIN, or a PART so we don't
+ // replay those if we are not currently in the channel they are from
+ // otherwise clients and state go a bit mad.
+ // Never replay them if they are from us.
+
+ // 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
+ char tokens[3][MAXDATASIZE];
+ char *token;
+
+ for (int i = 0; i < 3; i++) {
+ // Try to split
+ if ((token = strsep(&strcopy, " ")) == NULL) {
+ debugprint(DEBUG_CRIT, "sanitisereplay(): error splitting string on iteration '%d', returning!\n", i);
+ free(strcopyPtr);
+ return -1;
+ }
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(tokens[i], token, strlen(token) + 1);
+ }
+
+ if (strncmp(tokens[1], "TOPIC", strlen("TOPIC")) == 0 ||
+ strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0 ||
+ strncmp(tokens[1], "PART", strlen("PART")) == 0) {
+ // Skip over colon if present in channel name
+ int offset = 0;
+ if (tokens[2][0] == ':') {
+ offset = 1;
+ }
+
+ // To make sure it's not us
+ extractnickfromprefix(tokens[0], 1);
+
+ // Check if we're currently in this channel or if the log line is from us
+ if (!inchannel(channels, ircdstate->maxchannelcount, tokens[2] + offset) ||
+ ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0))) {
+ debugprint(DEBUG_FULL, "sanitisereplay(): Not sending '%s' replay line '%s'.\n", tokens[1], str);
+ free(strcopyPtr);
+ return 0;
+ }
+ }
+
+ // Separate special check for if a NICK change is from us but it isn't our current nick
+ if (strncmp(tokens[1], "NICK", strlen("NICK")) == 0) {
+ extractnickfromprefix(tokens[0], 1);
+
+ if ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0)) {
+ debugprint(DEBUG_FULL, "sanitisereplay(): Not sending '%s' replay line '%s'.\n", tokens[1], str);
+ free(strcopyPtr);
+ return 0;
+ }
+ }
+
+ free(strcopyPtr);
+ return 1;
+}
+
// Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem.
// 'basedir' is the directory in which to find 'replay.log'.
-int replaylines(int seconds, char *basedir) {
+int replaylinestime(int seconds, char *basedir) {
FILE *fp;
char str[MAXCHAR];
char filename[PATH_MAX];
@@ -287,35 +356,37 @@ int readreplayline(int seconds, int linenum, char *str, struct settings *setting
return 0;
}
-// Returns the number of seconds ago that 'nick' last spoke, or -1 if there is a problem.
+// Returns the line number in the replay log file on which '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) {
+long lastspokelinenumber(char *nick, char *basedir) {
FILE *fp;
- char str[MAXCHAR];
+ char line[MAXCHAR];
char filename[PATH_MAX];
// Build path
- snprintf(filename, PATH_MAX, "%s/replay.log", basedir);
-
- int lastspoketime = 0; // When 'nick' last spoke
+ if (!snprintf(filename, PATH_MAX, "%s/replay.log", basedir)) {
+ debugprint(DEBUG_CRIT, "lastspokelinenumber(): error: couldn't prepare replay path, exiting!\n");
+ exit(1);
+ }
- // Get the current time for comparison later
- int timenow = (int)time(NULL);
+ long linenumber = -1; // Line number where 'nick' last spoke
fp = fopen(filename, "r");
if (fp == NULL) {
- debugprint(DEBUG_FULL, "error: replaylineslastspoke(): could not open replay log '%s'.\n", filename);
+ debugprint(DEBUG_FULL, "error: lastspokelinenumber(): could not open replay log '%s'.\n", filename);
// Assume the file just doesn't exist yet - TODO - Interpret error codes to see what happened.
- return 0;
+ return -1;
}
- while (fgets(str, MAXCHAR, fp) != NULL) {
+ long curlinenumber = -1; // Current line number of this file reading loop
+ while (fgets(line, MAXCHAR, fp) != NULL) {
+ curlinenumber++;
// 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);
+ char *strcopy = strdup(line);
// Keep track of initial pointer for free()ing later
char *strcopyPtr = strcopy;
@@ -326,7 +397,7 @@ int lastspokesecondsago(char *nick, char *basedir) {
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);
+ debugprint(DEBUG_CRIT, "lastspokelinenumber(): error splitting string on iteration '%d', returning -1!\n", i);
return -1;
}
// Copy into the token array (strlen + 1 to get the NULL terminator)
@@ -337,14 +408,14 @@ int lastspokesecondsago(char *nick, char *basedir) {
// 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);
+ debugprint(DEBUG_CRIT, "lastspokelinenumber(): 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);
+ debugprint(DEBUG_CRIT, "lastspokelinenumber(): line didn't start with a timestamp, returning -1!\n", counter);
}
// Is it a PRIVMSG?
@@ -356,16 +427,136 @@ int lastspokesecondsago(char *nick, char *basedir) {
// Was it said by our 'nick'? Disable extractnickfromprefix() debugging
// as it gets very noisy when we call it from here.
extractnickfromprefix(tokens[1], 0);
- if ((strlen(tokens[1]) == strlen(nick)) && (strncmp(tokens[1], nick, strlen(nick)))) {
- // Not our 'nick', continue
+ if ((strlen(tokens[1]) == strlen(nick)) && (strncmp(tokens[1], nick, strlen(nick)) == 0)) {
+ // Our 'nick' found, set the line number
+ linenumber = curlinenumber;
+ }
+ }
+
+ debugprint(DEBUG_FULL, "lastspokelinenumber(): last spoke on line '%ld'.\n", linenumber);
+
+ fclose(fp);
+ return linenumber;
+}
+
+// Send the requested number of seconds worth of replay log lines to the requested client.
+// 'sourcefd' is the client to send to, and 'replayseconds' is the number of
+// seconds of replay log to replay.
+// Returns 1 for success or 0 for failure.
+int doreplaytime(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels) {
+ char outgoingmsg[MAXDATASIZE];
+
+ // Figure out how many lines to replay
+ int numlines = replaylinestime(replayseconds, settings->basedir);
+ debugprint(DEBUG_FULL, "doreplaytime(): Replay log lines: '%d'.\n", numlines);
+
+ if (numlines < 0) {
+ debugprint(DEBUG_CRIT, "doreplaytime(): Error getting number of replay lines.\n");
+ return 0;
+ } else if (numlines == 0) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ return 1;
+ }
+
+ // Announce the start
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Starting log replay....", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Replay those lines!
+ for (int i = 0; i < numlines; i++) {
+ int ret = readreplayline(replayseconds, i, outgoingmsg, settings, ircdstate);
+ if (ret == 0) {
+ debugprint(DEBUG_CRIT, "doreplaytime(): Error requesting replay line.\n");
+ return 0;
+ } else if (ret == -1) {
+ debugprint(DEBUG_FULL, "doreplaytime(): readreplayline() said to ignore replay line.\n");
continue;
}
- lastspoketime = timestamp;
+ if (sanitisereplay(outgoingmsg, ircdstate, channels) < 1) {
+ continue;
+ }
+
+ debugprint(DEBUG_FULL, "doreplaytime(): Sending replay line: '%s'.\n", outgoingmsg);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
}
- fclose(fp);
- return timenow - lastspoketime;
+ // Announce the end
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ return 1;
+}
+
+// Send replay log lines from line number 'linenumber' onwards, to client 'sourcefd'.
+// Returns 1 for success or 0 for failure.
+int doreplaylastspoke(int sourcefd, long linenumber, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels) {
+ debugprint(DEBUG_FULL, "doreplaylastspoke(): replaying from linenumber '%ld' to sourcefd '%d'.\n", linenumber, sourcefd);
+
+ FILE *fp;
+ char line[MAXCHAR];
+ char filename[PATH_MAX];
+ char outgoingmsg[MAXDATASIZE];
+
+ // Build path
+ if (!snprintf(filename, PATH_MAX, "%s/replay.log", settings->basedir)) {
+ debugprint(DEBUG_CRIT, "doreplaylastspoke(): error: couldn't prepare replay path, exiting!\n");
+ exit(1);
+ }
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ debugprint(DEBUG_FULL, "error: doreplaylastspoke(): could not open replay log '%s'.\n", filename);
+ // Assume the file just doesn't exist yet - TODO - Interpret error codes to see what happened.
+ return 0;
+ }
+
+ // Announce the start
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Starting log replay....", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ long curlinenumber = -1; // Current line number of this file reading loop
+ while (fgets(line, MAXCHAR, fp) != NULL) {
+ curlinenumber++;
+ // Skip through the file until line number 'linenumber'...
+ if (curlinenumber < linenumber) {
+ continue;
+ }
+ // ...carry on once we've reached 'linenumber'
+
+ // Read the timestamp from the line
+ int timestamp = gettimestamp(line);
+
+ // Make sure and it wasn't before blabouncer launched...
+ if (timestamp < ircdstate->launchtime) {
+ // Don't replay if this replay line happened before blabouncer launched,
+ // to avoid weird synchronisation issues with uncertain events from before
+ // we launched.
+ debugprint(DEBUG_FULL, "doreplaylastspoke(): Ignoring line '%s' from before we launched.\n", line);
+ continue;
+ }
+
+ // Insert our formatted [HH:MM:SS] timestamp into the message
+ formattime(line, settings->replaydates);
+
+ strncpy(outgoingmsg, line, strlen(line));
+ outgoingmsg[strlen(line)] = '\0';
+
+ if (sanitisereplay(outgoingmsg, ircdstate, channels) < 1) {
+ continue;
+ }
+
+ debugprint(DEBUG_FULL, "doreplaylastspoke(): Sending replay line: '%s'.\n", outgoingmsg);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ // Announce the end
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Log replay complete.", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ return 1;
}
// Write the line 'str' to the replay log file after prepending it with
diff --git a/replay.h b/replay.h
index 039c131..dcf6771 100644
--- a/replay.h
+++ b/replay.h
@@ -38,13 +38,25 @@
#define TIMELENF 11 // [HH:MM:SS] = 10 characters + 1 for null char
#define DATETIMELEN 50 // Should be enough for a full datetime string // TODO - Is it!? I think it will only ever be as long as [DD/MM/YY HH:MM:SS] (20 including null)
-int replaylines(int seconds, char *basedir);
+// Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem.
+// 'basedir' is the directory in which to find 'replay.log'.
+int replaylinestime(int seconds, char *basedir);
int readreplayline(int seconds, int linenum, char *str, struct settings *settings, struct ircdstate *ircdstate);
-// Returns the number of seconds ago that 'nick' last spoke, or -1 if there is a problem.
+// Returns the line number in the replay log file on which '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);
+long lastspokelinenumber(char *nick, char *basedir);
+
+// Send the requested number of seconds worth of replay log lines to the requested client.
+// 'sourcefd' is the client to send to, and 'replayseconds' is the number of
+// seconds of replay log to replay.
+// Returns 1 for success or 0 for failure.
+int doreplaytime(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels);
+
+// Send replay log lines from line number 'linenumber' onwards, to client 'sourcefd'.
+// Returns 1 for success or 0 for failure.
+int doreplaylastspoke(int sourcefd, long linenumber, struct client *clients, struct settings *settings, struct ircdstate *ircdstate, struct channel *channels);
int writereplayline(char *str, char *basedir);