shithub: ircd

Download patch

ref: eaf9a2c0694335ea7d5139f83b3824f6127fb310
parent: a3d7d3bbd391651dd9b6570480406c05e751eb45
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Jul 29 17:50:54 EDT 2025

adds first working channel functionality: JOIN and PRIVMSG

--- /dev/null
+++ b/chan.c
@@ -1,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+#include "ll.h"
+
+static Linked *channels = nil;
+
+static int
+findchanbyname(void *c, void *n)
+{
+	char *name = n;
+	IChan *ch = c;
+	return !strcmp(name, ch->name);
+}
+
+static int
+findclientinchannel(void *a, void *b)
+{
+	return a == b;
+}
+
+static void
+printusers(void *a, void *b)
+{
+	Client *c = a;
+	char *ch = b;
+	fprint(2, "  %s: %s\n", ch, c->nick);
+}
+
+static void
+printchannel(void *a, void*)
+{
+	IChan *ch = a;
+	fprint(2, "channel: %s\n", ch->name);
+	lforeach(&ch->users, printusers, ch->name);
+}
+
+IChan*
+joinchannel(Client *cl, char *name)
+{
+	IChan *c = lfind(&channels, findchanbyname, name);
+	if (!c) {
+		c = mallocz(sizeof(IChan), 1);
+		c->name = strdup(name);
+		ladd(&channels, c);
+	}
+	ladd(&c->users, cl);
+	ladd(&cl->channels, c);
+	if (debug > 2) {
+		lforeach(&channels, printchannel, nil);
+	}
+	return c;
+}
+
+IChan*
+findchannel(char *name)
+{
+	return lfind(&channels, findchanbyname, name);
+}
+
+int
+userinchannel(IChan *ch, Client *cl)
+{
+	return !!lfind(&ch->users, findclientinchannel, cl);
+}
--- a/cmd.c
+++ b/cmd.c
@@ -3,8 +3,11 @@
 #include "dat.h"
 #include "fns.h"
 #include "cmd.h"
+#include "ll.h"
 #include "version.h"
 
+static char channelprefix[] = "&#+!";
+
 static void
 cversion(Client *c, Request *r)
 {
@@ -102,19 +105,51 @@
 	}
 }
 
+typedef struct A_privmsgsend A_privmsgsend;
+struct A_privmsgsend {
+	char *msg;
+	IChan *chan;
+	Client *sender;
+};
+
 static void
+acprivmsgsend(void *a, void *b)
+{
+	Client *c = a;
+	A_privmsgsend *args = b;
+	ircsend(c, args->sender, Sprivmsg, args->chan->name, args->msg);
+}
+
+static void
 cprivmsg(Client *c, Request *r)
 {
 	/* (/lib/rfc/rfc2812:/^3.3.1) */
 	Client *tgt;
-	if (!r->args[0]) {
+	IChan *chan;
+	A_privmsgsend pmsg;
+	
+	if (!r->args[0] || r->args[0][0] == 0) {
 		reply(c, Enorecipient, "PRIVMSG");
 		return;
 	}
-	if (!r->args[1]) {
+	if (!r->args[1] || r->args[1][0] == 0) {
 		reply(c, Enotexttosend);
 		return;
 	}
+	if (strchr(channelprefix, r->args[0][0])) {
+		/* target is channel */
+		chan = findchannel(r->args[0]);
+		if (!(chan && userinchannel(chan, c))) {
+			reply(c, Ecannotsendtochan, r->args[0]);
+			return;
+		}
+		pmsg.chan = chan;
+		pmsg.msg = r->args[1];
+		pmsg.sender = c;
+		lforeach(&chan->users, acprivmsgsend, &pmsg);
+		return;
+	}
+	/* target is user */
 	tgt = findnick(r->args[0]);
 	if (!tgt) {
 		reply(c, Enosuchnick, r->args[0]);
@@ -144,7 +179,67 @@
 	reply(c, Runaway);
 }
 
+typedef struct A_listnames A_listnames;
+struct A_listnames {
+	IChan *channel;
+	Client *client;
+};
+
+/* Aux Cmd function */
 static void
+aclistnames(void *a, void *b)
+{
+	Client *c = a;
+	A_listnames *ln = b;
+	
+	reply(ln->client, Rnamreply, '=', ln->channel->name, c->nick);
+	ircsend(c, ln->client, Sjoin, ln->channel->name);
+}
+
+static void
+cjoin(Client *c, Request *r)
+{
+	/* (/lib/rfc/rfc2812:/^3.2.1) */
+	char *channel;
+	IChan *ch;
+	A_listnames ln;
+	
+	if (!r->args[0] || r->args[0][0] == 0) {
+		reply(c, Eneedmoreparams, "JOIN");
+		return;
+	}
+	for (int i = 0; i < 15;) {
+		/* i is channel, i+1 is ',' */
+		channel = r->args[i];
+		if (!channel)
+			break;
+		ch = joinchannel(c, channel);
+		ircsend(c, nil, Sjoin, channel);
+		
+		if (ch->topic) {
+			reply(c, Rtopic, channel, ch->topic);
+		} else {
+			reply(c, Rnotopic, channel);
+		}
+		
+		ln.client = c;
+		ln.channel = ch;
+		lforeach(&ch->users, aclistnames, &ln);
+		reply(c, Rendofnames, channel);
+		
+		if (r->args[i+1]) {
+			if (!strcmp(r->args[i+1], ",")) {
+				i += 2;
+				continue;
+			} else {
+				// TODO: syntax error?
+			}
+		} else
+			break;
+	}
+}
+
+static void
 cquit(Client *c, Request *r)
 {
 	/* (/lib/rfc/rfc2812:/^3.1.7) */
@@ -166,6 +261,7 @@
 	{ "nick", cnick },
 	{ "privmsg", cprivmsg },
 	{ "away", caway },
+	{ "join", cjoin },
 	{ "quit", cquit },
 };
 int ncommands = sizeof(commands) / sizeof(Command);
--- a/cmd.h
+++ b/cmd.h
@@ -28,11 +28,31 @@
 	.msg = "%s :End of WHOIS list",
 };
 
+Reply Rnotopic = {
+	.nr = 331,
+	.msg = "%s :No topic is set",
+};
+
+Reply Rtopic = {
+	.nr = 332,
+	.msg = "%s :%s",
+};
+
 Reply Rversion = {
 	.nr = 351,
 	.msg = "%s",
 };
 
+Reply Rnamreply = {
+	.nr = 353,
+	.msg = "%c%s :%s",
+};
+
+Reply Rendofnames = {
+	.nr = 366,
+	.msg = "%s :End of NAMES list",
+};
+
 Reply Enosuchnick = {
 	.nr = 401,
 	.msg = "%s :No such nick/channel",
@@ -43,6 +63,11 @@
 	.msg = "%s :No such server",
 };
 
+Reply Ecannotsendtochan = {
+	.nr = 404,
+	.msg = "%s :Cannot send to channel",
+};
+
 Reply Enorecipient = {
 	.nr = 411,
 	.msg = ":No recipient given (%s)",
@@ -80,4 +105,8 @@
 
 Reply Sprivmsg = {
 	.msg = "PRIVMSG %s :%s",
+};
+
+Reply Sjoin = {
+	.msg = "JOIN %s",
 };
--- a/dat.h
+++ b/dat.h
@@ -5,6 +5,7 @@
 typedef struct User User;
 typedef struct Replybuffer Replybuffer;
 typedef struct Client Client;
+typedef struct IChan IChan;
 
 #pragma varargck type "R" Request
 
@@ -55,6 +56,16 @@
 	User *user;
 	char *nick;
 	char *away;
+	
+	void *channels;
+};
+
+struct IChan
+{
+	char *name;
+	char *topic;
+	
+	void *users;
 };
 
 extern int debug;
--- a/fns.h
+++ b/fns.h
@@ -22,3 +22,7 @@
 Client* findclient(ulong);
 Client* findnick(char*);
 void delclient(Client*);
+
+IChan* joinchannel(Client*,char*);
+IChan* findchannel(char*);
+int userinchannel(IChan*,Client*);
--- a/ll.c
+++ b/ll.c
@@ -56,3 +56,10 @@
 	}
 	return nil;
 }
+
+void
+lforeach(Linked **ll, void (*f)(void*,void*), void *aux)
+{
+	for (Linked *l = *ll; l; l = l->next)
+		f(l->ptr, aux);
+}
--- a/ll.h
+++ b/ll.h
@@ -9,3 +9,4 @@
 void ladd(Linked**, void*);
 void ldel(Linked**, void*, void (*f)(void*));
 void* lfind(Linked**, int (*f)(void*,void*), void*);
+void lforeach(Linked**, void (*f)(void*,void*), void*);
--- a/mkfile
+++ b/mkfile
@@ -9,6 +9,7 @@
 	reply.$O\
 	users.$O\
 	client.$O\
+	chan.$O\
 	ll.$O\
 
 HFILES=fns.h dat.h cmd.h version.h ll.h
--- a/reply.c
+++ b/reply.c
@@ -52,9 +52,13 @@
 	va_list arg;
 	String *s;
 	
-	i = snprint(buf, sizeof buf, ":%s!%s@%s ",
-		sender->nick, sender->user->name,
-		sender->user->host ? sender->user->host : sysnameb);
+	if (sender) {
+		i = snprint(buf, sizeof buf, ":%s!%s@%s ",
+			sender->nick, sender->user->name,
+			sender->user->host ? sender->user->host : sysnameb);
+	} else {
+		i = snprint(buf, sizeof buf, ":%s ", sysnameb);
+	}
 	
 	va_start(arg, repl);
 	i += vsnprint(&buf[i], sizeof(buf) - i, repl.msg, arg);
--