summary refs log tree commit diff
diff options
context:
space:
mode:
authorNakidai <nakidai@disroot.org>2026-02-14 00:05:36 +0300
committerNakidai <nakidai@disroot.org>2026-02-14 00:20:38 +0300
commit834c85d80914007e31657d12d2f22a9cb2cd8624 (patch)
tree283ed0dacb635b97f93c21c8b0b94b6116ca29a3
parent15151c88a7eb381fb5b46daaf521fc609d40539c (diff)
downloadlibreircd-834c85d80914007e31657d12d2f22a9cb2cd8624.tar.gz
libreircd-834c85d80914007e31657d12d2f22a9cb2cd8624.zip
Add support for voice and +m channel mode
So now voices in code are not that useless. Also, code was fixed for
their printing as it is valid for a user to be both an oper and voiced
-rw-r--r--channel.c4
-rw-r--r--handle.c52
-rw-r--r--ircd.h1
-rw-r--r--reply.c6
4 files changed, 47 insertions, 16 deletions
diff --git a/channel.c b/channel.c
index f2da302..1b3024b 100644
--- a/channel.c
+++ b/channel.c
@@ -96,7 +96,7 @@ channel_modes_parse(const struct Message *msg)
 	{
 		for (m = msg->params[i]+1; *m; ++m) switch (*m)
 		{
-		break; case 'o':
+		break; case 'o': case 'v':
 			/* since all flags are ASCII (< 128) bit 7
 			 * can be and is abused for the set flag */
 			if (queue_r == lengthof(queue))
@@ -105,7 +105,7 @@ channel_modes_parse(const struct Message *msg)
 				return modes;
 			}
 			queue[queue_r++] = (set << 7) | *m;
-		break; case 'n': case 's': case 't':
+		break; case 'm': case 'n': case 's': case 't':
 			modes[modes_c].mode = *m;
 			modes[modes_c++].set = set;
 		break; default:
diff --git a/handle.c b/handle.c
index 684c2ee..1253cdf 100644
--- a/handle.c
+++ b/handle.c
@@ -182,6 +182,18 @@ mode_channel(struct Message *msg, struct Peer *peer)
 	parsed = channel_modes_parse(msg);
 	for (; parsed->param || parsed->mode; ++parsed) switch (parsed->mode)
 	{
+	break; case 'm':
+		announce_change(1);
+		if (parsed->set)
+			channel->modes |= CHANNEL_MODERATED;
+		else
+			channel->modes &= ~CHANNEL_MODERATED;
+	break; case 'n':
+		announce_change(1);
+		if (parsed->set)
+			channel->modes |= CHANNEL_NOFROMOUT;
+		else
+			channel->modes &= ~CHANNEL_NOFROMOUT;
 	break; case 'o':
 		ensure(parsed->param, reply(peer, 461, msg->command), 0);
 
@@ -195,12 +207,6 @@ mode_channel(struct Message *msg, struct Peer *peer)
 			channel->peers[i].modes |= CHANNEL_OPER;
 		else
 			channel->peers[i].modes &= ~CHANNEL_OPER;
-	break; case 'n':
-		announce_change(1);
-		if (parsed->set)
-			channel->modes |= CHANNEL_NOFROMOUT;
-		else
-			channel->modes &= ~CHANNEL_NOFROMOUT;
 	break; case 's':
 		/* TODO: implement +s. For now it's just a some mode with no param */
 		announce_change(1);
@@ -214,6 +220,19 @@ mode_channel(struct Message *msg, struct Peer *peer)
 			channel->modes |= CHANNEL_TOPIC;
 		else
 			channel->modes &= ~CHANNEL_TOPIC;
+	break; case 'v':
+		ensure(parsed->param, reply(peer, 461, msg->command), 0);
+
+		for (i = 0; i < channel->peers_c; ++i)
+			if (!strcmp(channel->peers[i].p->nick, parsed->param))
+				break;
+		ensure(i != channel->peers_c, reply(peer, 441, parsed->param, channel->name), 0);
+
+		announce_change(0);
+		if (parsed->set)
+			channel->peers[i].modes |= CHANNEL_VOICE;
+		else
+			channel->peers[i].modes &= ~CHANNEL_VOICE;
 	break; default:
 		modebuf[0] = parsed->mode;
 		modebuf[1] = 0;
@@ -295,7 +314,7 @@ names(struct Message *msg, struct Peer *peer)
 			chpp = &peer->channels[i]->peers[j];
 			if (chpp->modes & CHANNEL_OPER)
 				strlcat(buf, "@", sizeof(buf));
-			else if (chpp->modes & CHANNEL_VOICE)
+			if (chpp->modes & CHANNEL_VOICE)
 				strlcat(buf, "+", sizeof(buf));
 			strlcat(buf, peer->channels[i]->peers[j].p->nick, sizeof(buf));
 			if (strlen(buf) >= MESSAGE_MAX - 4*PEER_NICK_MAX
@@ -466,13 +485,18 @@ privmsg(struct Message *msg, struct Peer *peer)
 		ensure(i != channels_c, reply(peer, 401, msg->params[0]), 0);
 		ch = &channels[i];
 
-		if (ch->modes & CHANNEL_NOFROMOUT)
-		{
-			for (i = 0; i < ch->peers_c; ++i)
-				if (!strcmp(ch->peers[i].p->nick, peer->nick))
-					break;
-			ensure(i != ch->peers_c, reply(peer, 442, ch->name), 0);
-		}
+		for (i = 0; i < ch->peers_c; ++i)
+			if (ch->peers[i].p == peer)
+				break;
+
+		ensure(
+			(!(ch->modes & CHANNEL_NOFROMOUT) || i != ch->peers_c)
+			&& (!(ch->modes & CHANNEL_MODERATED)
+				|| ch->peers[i].modes & (CHANNEL_OPER | CHANNEL_VOICE)
+			),
+			reply(peer, 404, ch->name),
+			0
+		);
 
 		writechanf(
 			peer,
diff --git a/ircd.h b/ircd.h
index 3012783..fecfe60 100644
--- a/ircd.h
+++ b/ircd.h
@@ -83,6 +83,7 @@ struct Channel
 	size_t peers_c;
 	enum ChannelMode
 	{
+		BIT(CHANNEL_MODERATED),
 		BIT(CHANNEL_NOFROMOUT),
 		BIT(CHANNEL_SECRET),
 		BIT(CHANNEL_TOPIC),
diff --git a/reply.c b/reply.c
index 41fcb4a..e7f899b 100644
--- a/reply.c
+++ b/reply.c
@@ -196,6 +196,12 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		getnick(peer),
 		channel
 	), channel, _);
+	REPLY(404, WRITE(
+		":%s 404 %s %s :Cannot send to channel",
+		hostname,
+		getnick(peer),
+		channel
+	), channel, _);
 	REPLY(405, WRITE(
 		":%s 405 %s %s :You have joined too many channels",
 		hostname,