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);
--
⑨