summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-05-11 19:03:57 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-05-11 19:03:57 +0100
commit0559bff00a6be2054194632c3543bf62af1fb56f (patch)
treef007a35d1d7af9a7fa1166831b982c84111baad7
parentc46d01b5afd2da4779efb2700469037eca6122be (diff)
Add the ability to replay messages from a replay log file. (No replay log file writing yet.)
-rw-r--r--Makefile6
-rw-r--r--blabouncer.c27
-rw-r--r--blabouncer.conf3
-rw-r--r--config.c41
-rw-r--r--config.h2
-rw-r--r--replay.c231
-rw-r--r--replay.h21
7 files changed, 325 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index 3f8d034..5bf17a3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
CC=gcc
-DEPS = functions.h sockets.h config.h
+DEPS = functions.h sockets.h config.h replay.h
%.o: %.c $(DEPS)
$(CC) -Wall -Wextra -c -o $@ $<
-blabouncer: blabouncer.o functions.o sockets.o config.o
- $(CC) -Wall -Wextra -o blabouncer blabouncer.o functions.o sockets.o config.o
+blabouncer: blabouncer.o functions.o sockets.o config.o replay.o
+ $(CC) -Wall -Wextra -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o
diff --git a/blabouncer.c b/blabouncer.c
index ba9dca0..2e1f414 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -8,7 +8,9 @@
// - Add blabouncer MOTD (375, 372, 376)
// - "01:53:47 -!- ServerMode/#test [b] by irc.tghost.co.uk" on existing clients when new client connects
// - Keep track of changing user nicks/modes
-// - Relay log can just be "log/resend everything that ever hit sendto[all]client[s]()"
+// - Relay log can just be "log/resend everything that ever hit sendto[all]client[s]()" (or maybe just PRIVMSGs? + NOTICEs and friends?)
+// - Implement TLS
+// - Implement password/login
//
// Example WHOIS reply:
// BOUNCER-SERVER RECEIVED: :irc.tghost.co.uk 307 blabounce l_bratch :is identified for this nick
@@ -32,6 +34,7 @@
#include "functions.h"
#include "sockets.h"
#include "config.h"
+#include "replay.h"
#define SOURCE_SERVER 0
#define SOURCE_CLIENT 1
@@ -645,6 +648,28 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
sendtoclient(sourcefd, outgoingmsg);
}
+ // Send the client however many relay lines have been requested
+ int relaysecs = confrelayseconds();
+
+ // Figure out how many lines to relay
+ int numlines = relaylines(relaysecs);
+ printf("Replay log lines: '%d'.\n", numlines);
+
+ if (numlines < 0) {
+ printf("Error getting number of replay lines.\n");
+ exit(1);
+ }
+
+ // Relay those lines!
+ for (int i = 0; i < numlines; i++) {
+ if (!readrelayline(relaysecs, i, outgoingmsg)) {
+ printf("Error requesting relay line.\n");
+ exit(1);
+ }
+ printf("Sending relay line: '%s'.\n", outgoingmsg);
+ sendtoclient(sourcefd, outgoingmsg);
+ }
+
return 1;
}
diff --git a/blabouncer.conf b/blabouncer.conf
index 4f569dd..5b68918 100644
--- a/blabouncer.conf
+++ b/blabouncer.conf
@@ -7,3 +7,6 @@
nick = "blabounce"
username = "blabounce"
realname = "Mr Bla Bouncer"
+
+# How many seconds of relay log should be sent to connecting clients
+relayseconds = "7200"
diff --git a/config.c b/config.c
index ca6b158..7fec4de 100644
--- a/config.c
+++ b/config.c
@@ -1,5 +1,9 @@
#include "config.h"
+// TODO - Multiple functions here (at least readnames() and relayseconds() have the file opening code, rewrite.
+
+// TODO - Can isconf() and getconf() just be merged into one function?
+
// Check to see if a line is the configuration option specified by
// checking to see if str starts with conf followed by a space, a tab,
// or an equals sign.
@@ -69,9 +73,10 @@ int readnames(char *nick, char *username, char *realname) {
fp = fopen(filename, "r");
if (fp == NULL) {
- printf("error: could not open configuration file '%s.'", filename);
- return 1;
+ printf("error: could not open configuration file '%s'.\n", filename);
+ exit(1);
}
+
while (fgets(str, MAXCHAR, fp) != NULL) {
long int len;
if ((len = isconf(str, "nick"))) {
@@ -101,3 +106,35 @@ int readnames(char *nick, char *username, char *realname) {
fclose(fp);
return 1;
}
+
+// Return the relayseconds configuration option
+// (How many seconds of relay should be sent to connecting clients)
+int confrelayseconds() {
+ FILE *fp;
+ char str[MAXCHAR];
+ char* filename = "blabouncer.conf";
+
+ char secondsstr[MAXCHAR];
+ int seconds;
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ printf("error: could not open configuration file '%s'.\n", filename);
+ exit(1);
+ }
+
+ while (fgets(str, MAXCHAR, fp) != NULL) {
+ long int len;
+ if ((len = isconf(str, "relayseconds"))) {
+ getconf(str, len);
+ strncpy(secondsstr, str, strlen(str));
+ secondsstr[strlen(str)] = '\0';
+ printf("secondsstr is: '%s', length '%ld'.\n", secondsstr, strlen(secondsstr));
+ seconds = strtol(secondsstr, NULL, 10); // Convert resulting string to an integer, base 10
+ printf("seconds is: '%d'.\n", seconds);
+ }
+ }
+
+ return seconds;
+}
diff --git a/config.h b/config.h
index 5307178..57defb6 100644
--- a/config.h
+++ b/config.h
@@ -9,4 +9,6 @@
int readnames(char *nick, char *username, char *realname);
+int confrelayseconds();
+
#endif
diff --git a/replay.c b/replay.c
new file mode 100644
index 0000000..8c37d4c
--- /dev/null
+++ b/replay.c
@@ -0,0 +1,231 @@
+#include "replay.h"
+
+// Return the unixtime timestamp at the start of a line,
+// or -1 if a timestamp couldn't be extracted
+int gettimestamp(char *str) {
+ int timestamp;
+ char timestr[TIMELEN];
+ int count = 0;
+
+ // Make sure we're starting with a digit
+ if (!isdigit(str[0])) {
+ return -1;
+ }
+
+ // Extract each digit until we encounter a non-digit
+ for (int i = 0; i < TIMELEN; i++) {
+ if (isdigit(str[i])) {
+ timestr[count] = str[i];
+ count++;
+ }
+ }
+
+ timestr[count] = '\0'; // Null terminate
+
+ timestamp = strtol(timestr, NULL, 10); // Convert resulting string to an integer, base 10
+
+ return timestamp;
+}
+
+// Set 'str' to a string with the leading unixtime timestamp removed.
+// Returns 1 on success, 0 on failure
+int striptimestamp(char *str) {
+ char line[MAXCHAR];
+ int count = 0;
+
+ // Make sure we're starting with a digit
+ if (!isdigit(str[0])) {
+ return 0;
+ }
+
+ // Skip over each digit until we encounter a non-digit, record the position
+ for (int i = 0; i < TIMELEN; i++) {
+ if (isdigit(str[i])) {
+ count++;
+ }
+ }
+
+ // Skip over the space
+ count++;
+
+ int count2 = 0;
+
+ // Copy from the end of the digits (and the space) until the end of the string
+ for (size_t i = count; i < strlen(str); i++) {
+ line[count2] = str[i];
+ count2++;
+ }
+
+ strncpy(str, line, count2);
+ str[count2] = '\0';
+
+ return 1;
+}
+
+// Take a string like:
+// 1557592901 :foo!bar@baz PRIVMSG foo :hello world
+// And convert it to:
+// :foo!bar@baz PRIVMSG foo :[17:41:41] hello world
+// Only inserts the formatted time for PRIVMSGs at the moment (and maybe only needs to!).
+void formattime(char *str) {
+ // Extract the timestamp for conversion into [HH:MM:SS]
+ char timestr[TIMELEN];
+ sprintf(timestr, "%d", gettimestamp(str)); // Convert int time to string time
+ struct tm tm;
+ strptime(timestr, "%s", &tm);
+ char timestampf[TIMELEN]; // Formatted timestamp
+ // Convert into [HH:MM:SS]
+ strftime(timestampf, TIMELEN, "[%H:%M:%S]", &tm);
+ printf("Formatted time string: '%s'.\n", timestampf);
+
+ // Strip the original unixtimestamp
+ striptimestamp(str);
+ printf("Now got '%s' and '%s'.\n", timestampf, str);
+
+ // Take note of the length
+ int len = strlen(str);
+
+ // Find the start of the message if it's a PRIVMSG
+ char *ret;
+ int pos1;
+ if ((ret = strstr(str, "PRIVMSG")) != NULL) {
+ // Position within str of "PRIVMSG"
+ pos1 = ret - str;
+ printf("Found 'PRIVMSG' at position '%d' of '%s'.\n", pos1, str);
+ } else {
+ // If it's not a PRIVMSG, stop here
+ return;
+ }
+
+ char *ret2;
+ int pos2;
+ // Find the start of the actual message within the PRIVMSG
+ if ((ret2 = strstr(ret, ":")) != NULL) {
+ // Position within ret of ":"
+ pos2 = ret2 - ret;
+ printf("Found ':' at position '%d' of '%s'.\n", pos2, ret);
+ } else {
+ // Didn't find the real message, weird.
+ return;
+ }
+
+ // Position of start of PRIVMSG colon in original string
+ int realpos = pos1 + pos2 + 1;
+
+ // Build the new formatted string
+ char newline[MAXCHAR];
+
+ // First bit (:foo!bar@baz PRIVMSG foo :)
+ for (int i = 0; i < realpos; i++) {
+ newline[i] = str[i];
+ }
+
+ // Second bit (:foo!bar@baz PRIVMSG foo :[HH:MM:SS])
+ int j = 0;
+ for (int i = realpos; i < TIMELEN + realpos - 1; i++) { // -1 to avoid the null char from timestampf
+ newline[i] = timestampf[j];
+ j++;
+ }
+
+ // Insert a space after the formatted timestamp
+ newline[TIMELEN + realpos - 1] = ' ';
+
+ // Final bit (the original PRIVMSG contents)
+ for (int i = TIMELEN + realpos; i < len + TIMELEN; i++) {
+ newline[i] = str[i - TIMELEN];
+ }
+
+ // Copy the whole thing back to str and null terminate
+ strncpy(str, newline, len + TIMELEN);
+ str[len + TIMELEN] = '\0';
+
+ printf("Ended up with relay string of: '%s'.\n", str);
+}
+
+// Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem.
+int relaylines(int seconds) {
+ FILE *fp;
+ char str[MAXCHAR];
+ char* filename = "replay.log";
+
+ int numlines = 0;
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ printf("error: could not open replay log '%s'.\n", filename);
+ exit(1);
+ }
+
+ // Get the current time for comparison later
+ int timenow = (int)time(NULL);
+
+ while (fgets(str, MAXCHAR, fp) != NULL) {
+ // Read the timestamp from each line
+ int timestamp = gettimestamp(str);
+ if (timestamp < 1) {
+ printf("Error reading timestamp from replay log file.\n");
+ return -1;
+ }
+
+ printf("Timestamp is: '%d'.\n", timestamp);
+
+ // If the line is within range of the requested time, count it
+ if (timestamp > timenow - seconds) {
+ numlines++;
+ }
+ }
+
+ fclose(fp);
+ return numlines;
+}
+
+// Set 'str' to the line in the log with a timestamp of greater than 'seconds'
+// seconds ago, plus however many lines 'linenum' is set to.
+// Also modify the line to include a timestamp in the form "[HH:MM:SS]".
+// Returns 1 on success, or 0 on failure.
+// TODO - This is horribly inefficient since it re-reads the entire file each call, rewrite this!
+int readrelayline(int seconds, int linenum, char *str) {
+ FILE *fp;
+ char line[MAXCHAR];
+ char* filename = "replay.log";
+
+ int count = 0;
+
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ printf("error: could not open replay log '%s'.\n", filename);
+ return 1;
+ }
+
+ // Get the current time for comparison later
+ int timenow = (int)time(NULL);
+
+ while (fgets(line, MAXCHAR, fp) != NULL) {
+ printf("Read: '%s'.\n", line);
+ // Read the timestamp from each line
+ int timestamp = gettimestamp(line);
+ if (timestamp < 1) {
+ printf("Error reading timestamp from replay log file.\n");
+ exit(1);
+ }
+
+ // If the line is within range of the requested time...
+ 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
+ formattime(line);
+
+ strncpy(str, line, strlen(line));
+ str[strlen(line)] = '\0';
+ return 1;
+ }
+ count++;
+ }
+ }
+
+ // If we got here something went wrong
+ return 0;
+}
diff --git a/replay.h b/replay.h
new file mode 100644
index 0000000..e0b5822
--- /dev/null
+++ b/replay.h
@@ -0,0 +1,21 @@
+#ifndef REPLAY_H_INCLUDED
+#define REPLAY_H_INCLUDED
+
+#define _XOPEN_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#define MAXCHAR 1000
+#define TIMELEN 11 // 32-bit unixtime is up to 10 characters (+1 for null char) // TODO - Make this Year 2038 proof
+#define TIMELENF 11 // [HH:MM:SS] = 10 characters + 1 for null char
+
+int relaylines(int seconds);
+
+int readrelayline(int seconds, int linenum, char *str);
+
+int writerelayline(char *str);
+
+#endif