summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-10-26 04:57:33 -0400
committerJune McEnroe <june@causal.agency>2019-10-26 04:57:33 -0400
commit648c5cc21484a4f0d9846f04bc47c1e35fa18234 (patch)
tree02847d4c25ec01c65ce3db9b0ab2cc5e61dbb568
parentSet AWAY when no clients are connected (diff)
downloadpounce-648c5cc21484a4f0d9846f04bc47c1e35fa18234.tar.gz
pounce-648c5cc21484a4f0d9846f04bc47c1e35fa18234.zip
Track channel topics
-rw-r--r--bounce.c3
-rw-r--r--bounce.h1
-rw-r--r--pounce.17
-rw-r--r--state.c75
4 files changed, 58 insertions, 28 deletions
diff --git a/bounce.c b/bounce.c
index 648a17b..8d9a4b5 100644
--- a/bounce.c
+++ b/bounce.c
@@ -94,7 +94,7 @@ int main(int argc, char *argv[]) {
 	const char *away = "pounced :3";
 
 	int opt;
-	while (0 < (opt = getopt(argc, argv, "A:C:H:K:NTP:W:a:h:j:n:p:r:u:vw:"))) {
+	while (0 < (opt = getopt(argc, argv, "A:C:H:K:NP:W:a:h:j:n:p:r:u:vw:"))) {
 		switch (opt) {
 			break; case 'A': away = optarg;
 			break; case 'C': strlcpy(certPath, optarg, sizeof(certPath));
@@ -102,7 +102,6 @@ int main(int argc, char *argv[]) {
 			break; case 'K': strlcpy(privPath, optarg, sizeof(privPath));
 			break; case 'N': stateJoinNames = true;
 			break; case 'P': localPort = optarg;
-			break; case 'T': stateJoinTopic = true;
 			break; case 'W': clientPass = sensitive(optarg);
 			break; case 'a': auth = sensitive(optarg);
 			break; case 'h': host = optarg;
diff --git a/bounce.h b/bounce.h
index 4945931..26e62df 100644
--- a/bounce.h
+++ b/bounce.h
@@ -88,7 +88,6 @@ size_t clientDiff(const struct Client *client);
 void clientConsume(struct Client *client);
 
 bool stateJoinNames;
-bool stateJoinTopic;
 bool stateReady(void);
 void stateParse(char *line);
 void stateSync(struct Client *client);
diff --git a/pounce.1 b/pounce.1
index 95e554b..b76d819 100644
--- a/pounce.1
+++ b/pounce.1
@@ -8,7 +8,7 @@
 .
 .Sh SYNOPSIS
 .Nm
-.Op Fl NTv
+.Op Fl Nv
 .Op Fl A Ar away
 .Op Fl C Ar cert
 .Op Fl H Ar host
@@ -80,11 +80,6 @@ Bind to local
 .Ar port .
 The default port is 6697.
 .
-.It Fl T
-Request
-.Ql TOPIC
-for each channel when a client connects.
-.
 .It Fl W Ar pass
 Require the password
 .Ar pass
diff --git a/state.c b/state.c
index 02eaf72..a5bdaee 100644
--- a/state.c
+++ b/state.c
@@ -43,28 +43,45 @@ static void set(char **field, const char *value) {
 	if (!*field) err(EX_OSERR, "strdup");
 }
 
+struct Channel {
+	char *name;
+	char *topic;
+};
+
 static struct {
-	char **names;
+	struct Channel *ptr;
 	size_t cap, len;
-} chan;
+} chans;
 
 static void chanAdd(const char *name) {
-	if (chan.len == chan.cap) {
-		chan.cap = (chan.cap ? chan.cap * 2 : 8);
-		chan.names = realloc(chan.names, sizeof(char *) * chan.cap);
-		if (!chan.names) err(EX_OSERR, "realloc");
+	if (chans.len == chans.cap) {
+		chans.cap = (chans.cap ? chans.cap * 2 : 8);
+		chans.ptr = realloc(chans.ptr, sizeof(*chans.ptr) * chans.cap);
+		if (!chans.ptr) err(EX_OSERR, "realloc");
+	}
+	struct Channel *chan = &chans.ptr[chans.len++];
+	chan->name = strdup(name);
+	if (!chan->name) err(EX_OSERR, "strdup");
+	chan->topic = NULL;
+}
+
+static void chanTopic(const char *name, const char *topic) {
+	for (size_t i = 0; i < chans.len; ++i) {
+		if (strcmp(chans.ptr[i].name, name)) continue;
+		free(chans.ptr[i].topic);
+		chans.ptr[i].topic = strdup(topic);
+		if (!chans.ptr[i].topic) err(EX_OSERR, "strdup");
+		break;
 	}
-	chan.names[chan.len] = strdup(name);
-	if (!chan.names[chan.len]) err(EX_OSERR, "strdup");
-	chan.len++;
 }
 
 static void chanRemove(const char *name) {
-	for (size_t i = 0; i < chan.len; ++i) {
-		if (strcmp(chan.names[i], name)) continue;
-		free(chan.names[i]);
-		chan.names[i] = chan.names[--chan.len];
-		return;
+	for (size_t i = 0; i < chans.len; ++i) {
+		if (strcmp(chans.ptr[i].name, name)) continue;
+		free(chans.ptr[i].name);
+		free(chans.ptr[i].topic);
+		chans.ptr[i] = chans.ptr[--chans.len];
+		break;
 	}
 }
 
@@ -179,6 +196,18 @@ static void handleKick(struct Message *msg) {
 	}
 }
 
+static void handleTopic(struct Message *msg) {
+	if (!msg->params[0]) errx(EX_PROTOCOL, "TOPIC without channel");
+	if (!msg->params[1]) errx(EX_PROTOCOL, "TOPIC without topic");
+	chanTopic(msg->params[0], msg->params[1]);
+}
+
+static void handleReplyTopic(struct Message *msg) {
+	if (!msg->params[1]) errx(EX_PROTOCOL, "RPL_TOPIC without channel");
+	if (!msg->params[2]) errx(EX_PROTOCOL, "RPL_TOPIC without topic");
+	chanTopic(msg->params[1], msg->params[2]);
+}
+
 static void handleError(struct Message *msg) {
 	errx(EX_UNAVAILABLE, "%s", msg->params[0]);
 }
@@ -192,12 +221,14 @@ static const struct {
 	{ "003", handleReplyCreated },
 	{ "004", handleReplyMyInfo },
 	{ "005", handleReplyISupport },
+	{ "332", handleReplyTopic },
 	{ "CAP", handleCap },
 	{ "ERROR", handleError },
 	{ "JOIN", handleJoin },
 	{ "KICK", handleKick },
 	{ "NICK", handleNick },
 	{ "PART", handlePart },
+	{ "TOPIC", handleTopic },
 };
 
 void stateParse(char *line) {
@@ -260,10 +291,16 @@ void stateSync(struct Client *client) {
 		clientFormat(client, " :are supported by this server\r\n");
 	}
 
-	if (chan.len) assert(self.origin);
-	for (size_t i = 0; i < chan.len; ++i) {
-		clientFormat(client, ":%s JOIN %s\r\n", self.origin, chan.names[i]);
-		if (stateJoinTopic) serverFormat("TOPIC %s\r\n", chan.names[i]);
-		if (stateJoinNames) serverFormat("NAMES %s\r\n", chan.names[i]);
+	if (chans.len) assert(self.origin);
+	for (size_t i = 0; i < chans.len; ++i) {
+		const struct Channel *chan = &chans.ptr[i];
+		clientFormat(client, ":%s JOIN %s\r\n", self.origin, chan->name);
+		if (chan->topic) {
+			clientFormat(
+				client, ":%s 332 %s %s :%s\r\n",
+				Origin, self.nick, chan->name, chan->topic
+			);
+		}
+		if (stateJoinNames) serverFormat("NAMES %s\r\n", chan->name);
 	}
 }