summaryrefslogtreecommitdiff
path: root/replay.c
diff options
context:
space:
mode:
Diffstat (limited to 'replay.c')
-rw-r--r--replay.c233
1 files changed, 212 insertions, 21 deletions
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