ref: 2b69733d8c24af32a3e89fcf587e6a7409d179d6
parent: ab79325966de7ae86d5ed5f470930f103e252e6a
author: Michael Misch <michaelmisch1985@gmail.com>
date: Sun Feb 15 01:51:44 EST 2026
Add in IRC and some changes required to get it going - not quite ready for use
--- a/client.c
+++ b/client.c
@@ -346,7 +346,7 @@
return;
}
qlock(&b->l);
- if(f->cl->fd > 0){+ if(f->cl && f->cl->fd > 0){b->tag = flushtag = 1;
rwakeup(&b->rz);
close(f->cl->fd);
--- a/convS2C.c
+++ b/convS2C.c
@@ -23,12 +23,19 @@
convS2C(Cmd *cmd, char *c, uint n)
{struct state s;
+ int t;
s.fn = (*parse_cmd);
s.cmd.type = ErrorCmd;
s.base = c;
s.size = n;
- while((s.fn(&s) > 0));
+ for(;;){+ t = s.fn(&s);
+ if(t < 0)
+ return t;
+ if(t == 0)
+ break;
+ }
cmd->type = s.cmd.type;
cmd->data = s.cmd.data;
@@ -87,7 +94,7 @@
s->cmd.type = MarkdownCmd;
else {s->cmd.type = ServiceCmd;
- snprint(s->cmd.svccmd, n, s->base);
+ snprint(s->cmd.svccmd, n+1, s->base);
}
s->size -= n;
n++;
--- /dev/null
+++ b/irc/irc.c
@@ -1,0 +1,492 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <libsec.h>
+
+#define NPAR 14
+
+enum state {+ Time,
+ Cmd,
+ Prefix,
+ Middle,
+ Trail,
+ Ok
+};
+
+int enctls = 0; /* ssl/tls */
+int ctlfd;
+int ircfd;
+
+char *realname;
+char *nickname;
+char *server;
+char *svcname;
+
+QLock lk;
+
+typedef struct handler Handler;
+struct handler {+ char *cmd;
+ int (*fun)(int fd, char *time, char *pre, char *cmd, char *par[]);
+};
+
+int pmsg(int fd, char *time, char *pre, char *cmd, char *par[]);
+int ntc(int fd, char *time, char *pre, char *cmd, char *par[]);
+int generic(int fd, char *time, char *pre, char *cmd, char *par[]);
+int ping(int fd, char *time, char *pre, char *cmd, char *par[]);
+int join(int fd, char *time, char *pre, char *cmd, char *par[]);
+int misc(int fd, char *time, char *pre, char *cmd, char *par[]);
+int topic(int fd, char *time, char *pre, char *cmd, char *par[]);
+int numeric(int fd, char *time, char *pre, char *cmd, char *par[]);
+
+Handler handlers[] = {+ {"PRIVMSG", pmsg},+ {"NOTICE", ntc},+ {"PING", ping},+ {"PONG", ping},+ {"JOIN", join},+ {"PART", misc},+ {"MODE", misc},+ {"QUIT", misc},+ {"TOPIC", topic},+ {"332", numeric},+ {"333", numeric},+ {"352", numeric},+ {"315", numeric},+ {"311", numeric},+ {"319", numeric},+ {"312", numeric},+ {"320", numeric},+ {"317", numeric},+ {"318", numeric},+ {nil, nil},+};
+
+typedef struct command Command;
+struct command {+ char *cmd;
+ int (*fun)(char *buff, char *cmd, char *data);
+};
+
+int icmd(char *buff, char *cmd, char *data);
+int lcmd(char *buff, char *cmd, char *data);
+int jcmd(char *buff, char *cmd, char *data);
+int pcmd(char *buff, char *cmd, char *data);
+int qcmd(char *buff, char *cmd, char *data);
+int gcmd(char *buff, char *cmd, char *data);
+
+Command commands[] = {+ {"input", icmd},+ {"join", jcmd},+ {"list", lcmd},+ {"part", pcmd},+ {"quit", qcmd},+ {nil, nil}, +};
+
+int srvparse(char *line, char **time, char **pre, char **cmd, char *par[], int npar);
+int usrparse(char *line, char **pre, char **cmd, char **data);
+void ircsrv(void);
+void logger(void);
+void reconnect(void);
+
+void
+usage(void)
+{+ fprint(2, "usage: %s [-e] [-r realname] [-n nickname] -s svcname [net!]ircserver[!port]\n", argv0);
+ exits("usage");+}
+
+void
+killall(void)
+{+ postnote(PNGROUP, getpid(), "quit");
+ while(waitpid() != -1)
+ ;
+ remove(svcname);
+ exits(nil);
+}
+
+void
+die(void *, char*)
+{+ killall();
+}
+
+void
+main(int argc, char **argv)
+{+ nickname = getuser();;
+
+ ARGBEGIN{+ case 'e':
+ enctls = 1;
+ break;
+ case 'r':
+ realname = EARGF(usage());
+ break;
+ case 's':
+ svcname = EARGF(usage());
+ break;
+ case 'n':
+ nickname = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc != 1 || !svcname)
+ usage();
+
+ server = argv[0];
+
+ /* Create service */
+ ctlfd = open("/mnt/svc/clone", ORDWR);+ if(ctlfd < 0)
+ sysfatal("%r");+
+ reconnect();
+ print("Connection to %s established\n", server);+
+ if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) {+ notify(die);
+
+ switch(rfork(RFPROC|RFMEM|RFFDG)) {+ case -1:
+ sysfatal("rfork: %r");+ case 0:
+ notify(die);
+ logger();
+ break;
+ default:
+ /* Name service */
+ print("Creating service\n");+ fprint(ctlfd, "%s\n", svcname);
+ svcname = smprint("/srv/%s", svcname);+
+ /* Default buffer for server messages */
+ fprint(ctlfd, "create server\n");
+ ircsrv();
+ killall();
+ break;
+ }
+ }
+
+ exits(nil);
+}
+
+static long
+readln(int fd, void *vp, long len)
+{+ char *b = vp;
+ while(len > 0 && read(fd, b, 1) > 0){+ if(*b++ == '\n')
+ break;
+ len--;
+ }
+ return b - (char*)vp;
+}
+
+void
+reconnect(void)
+{+ TLSconn *conn;
+ if(ircfd >= 0)
+ close(ircfd);
+ if((ircfd = dial(netmkaddr(server, "tcp", "6667"), nil, nil, nil)) < 0)
+ sysfatal("dial: %r");+ if(enctls > 0) {+ conn = (TLSconn *)mallocz(sizeof *conn, 1);
+ ircfd = tlsClient(ircfd, conn);
+ if(ircfd < 0)
+ sysfatal("tls: %r");+ }
+ // TODO: factotum
+ //fprint(ircfd, "PASS %s\r\n", passwd);
+ fprint(ircfd, "USER %s nil nil :%s\r\n", nickname, realname);
+ fprint(ircfd, "NICK %s\r\n", nickname);
+}
+
+void
+logger(void)
+{+ char buf[513];
+ char *time, *pre, *cmd, *par[NPAR];
+ long n;
+
+ for(;;){+ while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){+ buf[n] = 0;
+ if(!srvparse(buf, &time, &pre, &cmd, par, NPAR)) {+ Handler *hp = handlers;
+ while(hp->cmd != nil) {+ if(!strcmp(hp->cmd, cmd)) {+ hp->fun(0, time, pre, cmd, par);
+ break;
+ }
+ ++hp;
+ }
+ if(hp->cmd == nil)
+ generic(0, time, pre, cmd, par);
+ }
+ }
+ reconnect();
+ }
+}
+
+void
+ircsrv(void)
+{+ char buf[512];
+ char *pre, *cmd, *data;
+ int n;
+
+ for(;;){+ n = read(ctlfd, buf, sizeof(buf)-1);
+ buf[n] = '\0';
+print("Cmd in: %s\n", buf);+ if(!usrparse(buf, &pre, &cmd, &data)){+ Command *c = commands;
+print("Cmd found: %s\n", cmd);+ while(c->cmd != nil){+
+ if(!cistrcmp(c->cmd, cmd)){+ c->fun(pre, cmd, data);
+ break;
+ }
+ ++c;
+ }
+ if(c->cmd == nil){+ /* Unknown command, pass through to IRC */
+ gcmd(pre, cmd, data);
+ }
+ }
+ }
+}
+
+static char *
+prenick(char *p)
+{+ char *n = p;
+ if(p != nil){+ while(*p != '\0' && *p != '!')
+ ++p;
+ if(*p != '!')
+ n = nil;
+ *p = '\0';
+ }
+ return n;
+}
+
+int
+pmsg(int, char *, char *pre, char *, char *par[])
+{+ print("pmsg pre=%s, par=%s\n", pre, par[0]);+ return 1;
+}
+
+int
+ntc(int, char *, char *pre, char *, char *par[])
+{+ return fprint(ctlfd, "feed server\nnotice pre=%s par[0]=%s\n", pre, par[0]);
+}
+
+int
+generic(int, char *time, char *pre, char *cmd, char *par[])
+{+ char *nick = prenick(pre);
+
+ // TODO: Build out the whole message here
+ if(nick != nil)
+ return fprint(ctlfd, "feed server\ngeneric %s %s (%s)\n", time, cmd, nick);
+ else
+ return fprint(ctlfd, "feed server\ngeneric %s %s (%s)\n", time, cmd, par[0]);
+}
+
+int
+ping(int, char *, char *, char *, char *par[])
+{+ return fprint(ircfd, "PONG %s\r\n", par[0]);
+}
+
+int
+join(int, char *, char *, char *, char *par[])
+{+ if (cistrcmp(par[0], nickname))
+ return fprint(ctlfd, "create %s\n", par[1]);
+ return fprint(ctlfd, "feed %s\n%s\n", par[0], par[1]);
+}
+
+int
+misc(int, char *, char *pre, char *, char *par[])
+{+
+ return fprint(ircfd, "feed server\nmisc %s %s\n", pre, par[0]);
+}
+
+int
+numeric(int i, char *time, char *pre, char *cmd, char *par[])
+{+ /* Start to handle all of these - Important ones */
+print("Numeric: %s %s\n", cmd, pre);+ return 0;
+}
+
+int
+topic(int, char *, char *pre, char *, char *par[])
+{+ return fprint(ctlfd, "title %s\n%s %s\n", pre, par[0], par[1]);
+}
+
+int
+icmd(char *buff, char *, char *data)
+{+ return fprint(ircfd, "PRIVMSG %s :%s\r\n", buff, data);
+}
+
+int
+jcmd(char *buff, char *, char *)
+{+print("joining %s\n", buff);+ fprint(ctlfd, "create %s\n", buff);
+ return fprint(ircfd, "JOIN %s\r\n", buff);
+}
+
+int
+pcmd(char *buff, char *, char *)
+{+ return fprint(ircfd, "PART %s\r\n", buff);
+}
+
+int
+lcmd(char *buff, char *, char *)
+{+ return fprint(ircfd, "LIST %s\r\n", buff);
+}
+
+int
+qcmd(char *, char *, char *data)
+{+ fprint(ircfd, "QUIT: %s\r\n", data);
+ fprint(ctlfd, "quit\n");
+ exits(0);
+}
+
+int
+gcmd(char *buff, char *cmd, char *data)
+{+ /* General command, push to ircfd */
+ return fprint(ircfd, "%s %s :%s\r\n", cmd, buff, data);
+}
+
+int
+usrparse(char *line, char **pre, char **cmd, char **data)
+{+ char *p;
+
+ enum state st = Cmd;
+ *pre = *data = nil;
+
+ for(*cmd = p = line; *p != '\0'; ++p){+ switch(st){+ case Cmd:
+ if(*p == ' '){+ *p = '\0';
+ *pre = p + 1;
+ st = Prefix;
+ }
+ break;
+ case Prefix:
+ if(*p == '\n' || *p == '\r'){+ *p = '\0';
+ *data = p + 1;
+ st = Middle;
+ }
+ /* No data */
+ if(*p == '\0'){+ *p = '\0';
+ st = Ok;
+ }
+ break;
+ case Middle:
+ if(*p == '\r' || *p == '\n' || *p == '\0'){+ *p = '\0';
+ st = Ok;
+ }
+ break;
+ case Ok:
+ *p = '\0';
+ break;
+ }
+ }
+ return st == Ok ? 0 : 1;
+}
+
+int
+srvparse(char *line, char **time, char **pre, char **cmd, char *par[], int npar)
+{+ int i;
+ char *p;
+
+ enum state st = Time;
+
+ *time = *pre = *cmd = nil;
+
+ for(*time = p = line, i = 0; *p != '\0'; ++p){+ switch(st){+ case Time:
+ if(*p == ' ') {+ *p = '\0';
+ *cmd = p + 1;
+ st = Cmd;
+ }
+ break;
+ case Cmd:
+ if(*p == ':') {+ *p = '\0';
+ *pre = p + 1;
+ st = Prefix;
+ } else if (*p == ' ') {+ *p = '\0';
+ par[i] = p + 1;
+ st = Middle;
+ }
+ break;
+ case Prefix:
+ if(*p == ' '){+ *p = '\0';
+ *cmd = p + 1;
+ st = Cmd;
+ }
+ break;
+ case Middle:
+ if(*p == '\r' || *p == '\n'){+ *p = '\0';
+ st = Ok;
+ } else if (*p == ':'){+ *p = '\0';
+ par[i] = p + 1;
+ st = Trail;
+ } else if (*p == ' '){+ *p = '\0';
+ i = (i + 1) % npar;
+ par[i] = p + 1;
+ st = Middle;
+ }
+ break;
+ case Trail:
+ if(*p == '\r' || *p == '\n'){+ *p = '\0';
+ st = Ok;
+ }
+ break;
+ case Ok:
+ *p = '\0';
+ break;
+ }
+ }
+ par[(i + 1) % npar] = nil;
+ return st == Ok ? 0 : 1;
+}
+
--- /dev/null
+++ b/irc/mkfile
@@ -1,0 +1,11 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/svc
+
+TARG=irc
+
+OFILES=\
+ irc.$O
+
+</sys/src/cmd/mkone
+
--- a/service.c
+++ b/service.c
@@ -340,25 +340,16 @@
respond(r, nil);
return;
case Qctl:
- /* NOTE: This stays here so we always get a good ID back on the client from the initial read */
- if(!f->svc->isInitialized) {- snprint(buf, sizeof(buf), "%d\n", SERVICEID(f->svc));
- readstr(r, buf);
- respond(r, nil);
- return;
- } else {- /* Wait for any data/command from the client */
- srvrelease(fs);
- while((cmd = recvp(f->svc->cmds)) == nil)
- ;
- srvacquire(fs);
- if(cmd->type != FlushCmd){- n = snprint(r->ofcall.data, r->ifcall.count, "%C", cmd);
- r->ofcall.count = n;
- }
- respond(r, nil);
- return;
+ /* Wait for any data/command from the client */
+ srvrelease(fs);
+ cmd = recvp(f->svc->cmds);
+ if(cmd && cmd->type != FlushCmd){+ n = snprint(r->ofcall.data, r->ifcall.count, "%C", cmd);
+ r->ofcall.count = n;
}
+ srvacquire(fs);
+ respond(r, nil);
+ return;
}
respond(r, "not implemented");
@@ -382,7 +373,11 @@
n = r->ofcall.count = r->ifcall.count;
p = mallocz(n, 1);
memmove(p, r->ifcall.data, n);
- convS2C(&cmd, p, n);
+ n = convS2C(&cmd, p, n);
+ if(n <= 0){+ respond(r, "malformed command");
+ goto Out;
+ }
switch(cmd.type){case CloneCmd:
if(!f->svc->isInitialized){@@ -414,7 +409,7 @@
}
goto Out;
}
- // Ignore, something weird going on.
+ /* Ignore, something weird going on */
respond(r, nil);
goto Out;
case CreateCmd:
--
⑨