diff --git a/install/install-neindaw.rc b/install/install-neindaw.rc new file mode 100644 index 0000000..a36f4e6 --- /dev/null +++ b/install/install-neindaw.rc @@ -0,0 +1,6 @@ +#!/bin/rc +mkdir -p $home/code +cd $home/code +git/clone https://git@git.sr.ht/~ft/neindaw +cd neindaw +mk install diff --git a/install/irc7/.hg/00changelog.i b/install/irc7/.hg/00changelog.i new file mode 100644 index 0000000..d3a8311 Binary files /dev/null and b/install/irc7/.hg/00changelog.i differ diff --git a/install/irc7/.hg/branch b/install/irc7/.hg/branch new file mode 100644 index 0000000..4ad96d5 --- /dev/null +++ b/install/irc7/.hg/branch @@ -0,0 +1 @@ +default diff --git a/install/irc7/.hg/branchheads.cache b/install/irc7/.hg/branchheads.cache new file mode 100644 index 0000000..170a5de --- /dev/null +++ b/install/irc7/.hg/branchheads.cache @@ -0,0 +1,2 @@ +ac16a1e78cdfc12ffd5af66949e50846e580c5e5 1 +ac16a1e78cdfc12ffd5af66949e50846e580c5e5 default diff --git a/install/irc7/.hg/dirstate b/install/irc7/.hg/dirstate new file mode 100644 index 0000000..bb472af Binary files /dev/null and b/install/irc7/.hg/dirstate differ diff --git a/install/irc7/.hg/hgrc b/install/irc7/.hg/hgrc new file mode 100644 index 0000000..92f324a --- /dev/null +++ b/install/irc7/.hg/hgrc @@ -0,0 +1,2 @@ +[paths] +default = https://code.9front.org/hg/irc7 diff --git a/install/irc7/.hg/requires b/install/irc7/.hg/requires new file mode 100644 index 0000000..5175383 --- /dev/null +++ b/install/irc7/.hg/requires @@ -0,0 +1,3 @@ +revlogv1 +store +fncache diff --git a/install/irc7/.hg/store/00changelog.i b/install/irc7/.hg/store/00changelog.i new file mode 100644 index 0000000..19de1ed Binary files /dev/null and b/install/irc7/.hg/store/00changelog.i differ diff --git a/install/irc7/.hg/store/00manifest.i b/install/irc7/.hg/store/00manifest.i new file mode 100644 index 0000000..48a969a Binary files /dev/null and b/install/irc7/.hg/store/00manifest.i differ diff --git a/install/irc7/.hg/store/data/irc.c.i b/install/irc7/.hg/store/data/irc.c.i new file mode 100644 index 0000000..a600c4f Binary files /dev/null and b/install/irc7/.hg/store/data/irc.c.i differ diff --git a/install/irc7/.hg/store/data/irc.h.i b/install/irc7/.hg/store/data/irc.h.i new file mode 100644 index 0000000..d6c0673 Binary files /dev/null and b/install/irc7/.hg/store/data/irc.h.i differ diff --git a/install/irc7/.hg/store/data/irc.man.i b/install/irc7/.hg/store/data/irc.man.i new file mode 100644 index 0000000..1f9f9d7 Binary files /dev/null and b/install/irc7/.hg/store/data/irc.man.i differ diff --git a/install/irc7/.hg/store/data/ircsrv.c.i b/install/irc7/.hg/store/data/ircsrv.c.i new file mode 100644 index 0000000..78c2f64 Binary files /dev/null and b/install/irc7/.hg/store/data/ircsrv.c.i differ diff --git a/install/irc7/.hg/store/data/mkfile.i b/install/irc7/.hg/store/data/mkfile.i new file mode 100644 index 0000000..419db1c Binary files /dev/null and b/install/irc7/.hg/store/data/mkfile.i differ diff --git a/install/irc7/.hg/store/fncache b/install/irc7/.hg/store/fncache new file mode 100644 index 0000000..0742b83 --- /dev/null +++ b/install/irc7/.hg/store/fncache @@ -0,0 +1,5 @@ +data/irc.c.i +data/irc.h.i +data/irc.man.i +data/ircsrv.c.i +data/mkfile.i diff --git a/install/irc7/.hg/store/undo b/install/irc7/.hg/store/undo new file mode 100644 index 0000000..12223f4 Binary files /dev/null and b/install/irc7/.hg/store/undo differ diff --git a/install/irc7/.hg/undo.branch b/install/irc7/.hg/undo.branch new file mode 100644 index 0000000..331d858 --- /dev/null +++ b/install/irc7/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/install/irc7/.hg/undo.dirstate b/install/irc7/.hg/undo.dirstate new file mode 100644 index 0000000..e69de29 diff --git a/install/irc7/6.irc b/install/irc7/6.irc new file mode 100755 index 0000000..5c5191b Binary files /dev/null and b/install/irc7/6.irc differ diff --git a/install/irc7/6.ircsrv b/install/irc7/6.ircsrv new file mode 100755 index 0000000..494f7b7 Binary files /dev/null and b/install/irc7/6.ircsrv differ diff --git a/install/irc7/irc.6 b/install/irc7/irc.6 new file mode 100644 index 0000000..81f82d9 Binary files /dev/null and b/install/irc7/irc.6 differ diff --git a/install/irc7/irc.c b/install/irc7/irc.c new file mode 100644 index 0000000..4be275a --- /dev/null +++ b/install/irc7/irc.c @@ -0,0 +1,854 @@ +#include +#include +#include +#include + +char help[] = +"cmd explanation/example\n" +"--------------------------------------------\n" +"/m privmsg #chan/nick message\n" +"/M mode #chan +nt\n" +"/j join #chan\n" +"/p part #chan\n" +"/q send parameters raw to the server\n" +"/l list #chan\n" +"/n nick newnick\n" +"/N notice #chan/nick message\n" +"/t set victim\n" +"/T topic #chan newtopic\n" +"/W whois nick\n" +"/w who nick (a shorter whois)\n"; + +#define NPAR 14 + +enum state { Time, Cmd, Prefix, Middle, Trail, Ok }; + +typedef struct handler Handler; + +struct handler { + char *cmd; + int (*fun)(int fd, char *time, char *pre, char *cmd, char *par[]); +}; + +QLock lck; +int server_in; +int server_out; +int scr; +char *victim; +char *nick; +int inacme; /* running in acme? */ +int linewidth; /* terminal width in # of characters */ + +int replay; /* just print the log ma'am */ + +void setwintitle(char *chan); + +int rtcs(int fd, char *cset); +int wtcs(int fd, char *cset); +int follow(int fd); +void getwidth(void); /* establish the width of the terminal, from mc.c */ + +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 misc(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}, + {"JOIN", misc}, + {"PART", misc}, + {"MODE", misc}, + {"QUIT", misc}, + {"TOPIC", misc}, + {"332", numeric}, + {"333", numeric}, + {"352", numeric}, + {"315", numeric}, + {"311", numeric}, + {"319", numeric}, + {"312", numeric}, + {"320", numeric}, + {"317", numeric}, + {"318", numeric}, + {nil, nil} +}; + +int srvparse(char *line, char **time, char **pre, char **cmd, char *par[], int npar); +int usrparse(char *ln, char *cmd, char *par[], int npar); + +void +usage(void) +{ + char usage[] = "usage: irc [-c charset] [-t victim] [-b lines] [-r file] [/srv/irc [/tmp/irc]]\n"; + write(1, usage, sizeof(usage)-1); + exits("usage"); +} + +void +setwintitle(char *chan) +{ + int fd; + + if ((fd = open("/dev/label", OWRITE)) >= 0) { + fprint(fd, "%s", chan); + close(fd); + } + if ((fd = open("/dev/acme/ctl", OWRITE)) >= 0) { + fprint(fd, "name -IRC/%s\n", chan); + close(fd); + inacme = 1; + } +} + +/* try to find out whether we're running in acme's win -e */ +int +testacme(void) +{ + return access("/dev/acme", OREAD) >= 0 ? 1 : 0; +} + +void +usrin(void) +{ + char *line, *p; + char *par[2]; + char cmd; + int n, i; + + Biobuf kbd; + Binit(&kbd, 0, OREAD); + while ((line = Brdstr(&kbd, '\n', 0)) != nil) { + n = utflen(line); + if(!inacme) { + p = malloc(n); + for (i = 0; i < n; ++i) + p[i] = '\b'; + write(scr, p, i); + free(p); + } + qlock(&lck); + if (!usrparse(line, &cmd, par, 2)) { + switch(cmd) { + case 'q': /* quote, just send the params ... */ + if(par[0]) { + fprint(server_out, "%s %s\r\n", par[0], par[1] ? par[1] : ""); + } else { + fprint(scr, "/q %s %s: not enough arguments\n", par[0], par[1]); + } + break; + case 'M': + if(par[0] && par[1]) { + fprint(server_out, "MODE %s %s\r\n", par[0], par[1]); + } else { + fprint(scr, "/M %s %s: not enough arguments\n", par[0], par[1]); + } + break; + case 'm': + if(par[0] && par[1]) { + fprint(server_out, "PRIVMSG %s :%s\r\n", par[0], par[1]); + } else { + fprint(scr, "/m %s %s: not enough arguments\n", par[0], par[1]); + } + break; + case 't': + if(par[0] != nil) { + free(victim); + victim = strdup(par[0]); + setwintitle(par[0]); + } + fprint(scr, "*** default victim set to '%s'\n", par[0]); + break; + case 'T': + if(par[0] == nil) + fprint(server_out, "TOPIC %s\r\n", victim); + else if(par[1] == nil) + fprint(server_out, "TOPIC %s\r\n", par[0]); + else + fprint(server_out, "TOPIC %s :%s\r\n", par[0], par[1]); + break; + case 'j': + fprint(server_out, "JOIN %s\r\n", par[0] == nil ? victim : par[0]); + break; + case 'p': + fprint(server_out, "PART %s\r\n", par[0] == nil ? victim : par[0]); + break; + case 'n': + if(par[0] != nil) { + fprint(server_out, "NICK %s\r\n", par[0]); + free(nick); + nick = strdup(par[0]); + } else { + fprint(scr, "%s", help); + } + break; + case 'N': + if(par[1] != nil) + fprint(server_out, "NOTICE %s :%s\r\n", par[0] == nil ? victim : par[0], par[1]); + break; + case 'W': + fprint(server_out, "WHOIS %s %s\r\n", par[0] == nil ? victim : par[0], par[0]); + case 'w': + fprint(server_out, "WHO %s\r\n", par[0] == nil ? victim : par[0]); + break; + case 'l': + fprint(server_out, "LIST %s\r\n", par[0] == nil ? victim : par[0]); + break; + case 'L': + fprint(server_out, "NAMES %s\r\n", par[0] == nil ? victim : par[0]); + break; + case 'f': + break; + case 'h': + case 'H': + fprint(scr, "%s", help); + break; + } + } else { + fprint(scr, "%s", help); + } + qunlock(&lck); + free(line); + } + exits(0); +} + +void +timestamp(char *logtime, char *scrtime, int maxlen) +{ + static char *wday[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + }; + static char *mon[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + static int day = 0; + Tm *t; + + t = localtime(atol(logtime)); + + if (t->mday != day) { + day = t->mday; + fprint(scr, "-- %s, %02d %s %d --\n", + wday[t->wday], t->mday, mon[t->mon], + t->year + 1900); + } + + snprint(scrtime, maxlen, "%02d:%02d:%02d", + t->hour, t->min, t->sec); +} + +void +srvin(void) +{ + char *line; + char *time, *pre, *cmd, *par[NPAR]; + char scrtime[32]; + Biobuf srv; + Binit(&srv, server_in, OREAD); + + while ((line = Brdstr(&srv, '\n', 0)) != nil) { + if (!srvparse(line, &time, &pre, &cmd, par, NPAR)) { + Handler *hp = handlers; + qlock(&lck); + timestamp(time, scrtime, sizeof(scrtime)); + while (hp->cmd != nil) { + if (!strcmp(hp->cmd, cmd)) { + hp->fun(server_out, scrtime, pre, cmd, par); + break; + } + ++hp; + } + if (hp->cmd == nil) + generic(server_out, scrtime, pre, cmd, par); + qunlock(&lck); + } + free(line); + } +} + +void +replayfile(void) +{ + char *line; + char *time, *pre, *cmd, *par[NPAR]; + char scrtime[32]; + Biobuf srv; + Binit(&srv, server_in, OREAD); + + while ((line = Brdstr(&srv, '\n', 0)) != nil) { + if (!srvparse(line, &time, &pre, &cmd, par, NPAR)) { + Handler *hp = handlers; + qlock(&lck); + timestamp(time, scrtime, sizeof(scrtime)); + while (hp->cmd != nil) { + if (!strcmp(hp->cmd, cmd)) { + hp->fun(server_out, scrtime, pre, cmd, par); + break; + } + ++hp; + } + if (hp->cmd == nil) + generic(server_out, scrtime, pre, cmd, par); + qunlock(&lck); + } + free(line); + } +} + +/* + * display the last N lines from the conversation + * if we have a default target only the conversation with + * that target will be shown + */ +void +seekback(int fd, int lines) +{ + Biobuf srv; + int found = 0, off; + char c, *line; + + if(lines < 0) + return; + + Binit(&srv, fd, OREAD); + + Bseek(&srv, -2, 2); + while(((off = Boffset(&srv)) > 0) && found < lines) { + c = Bgetc(&srv); + Bungetc(&srv); + if(c == '\n') { + Bseek(&srv, 1, 1); + line = Brdstr(&srv, '\n', '\0'); + if(victim) { + if(cistrstr(line, victim)) + found++; + } else { + found++; + } + free(line); + } + Bseek(&srv, off-1, 0); + } + + Bterm(&srv); +} + +void +main(int argc, char *argv[]) +{ + char *charset = nil; + char buf[32], buf2[32], *out = nil, *in = nil; + char *arg; + int sb = 10; /* how many lines are we displaying initially */ + int uipid; + + ARGBEGIN { + case 't': + victim = strdup(EARGF(usage())); + break; + case 'b': + arg = ARGF(); + if(arg != nil && arg[0] != '-') + sb = atoi(arg); + else + sb = 0; /* show all text */ + break; + case 'c': + charset = EARGF(usage()); + break; + case 'r': + replay = 1; + sb = 0; + break; + default: + usage(); + } ARGEND; + + switch(argc) { + case 0: + break; + case 1: + if(replay) + in = argv[0]; + else + out = argv[0]; + break; + case 2: + out = argv[0]; + in = argv[1]; + break; + default: + usage(); + } + + if(out == nil) { + out = getuser(); + if(strlen(out) > 4) + out[4] = 0; + snprint(buf, sizeof buf, "/srv/%sirc", out); + out = buf; + } + if(in == nil) { + in = getuser(); + if(strlen(in) > 4) + in[4] = 0; + snprint(buf2, sizeof buf2, "/tmp/%sirc", in); + in = buf2; + } + + if(!replay && (server_out = open(out, OWRITE)) < 0) + sysfatal("open write: %s %r", out); + if ((server_in = open(in, OREAD)) < 0) + sysfatal("open read: %s %r", in); + + inacme = testacme(); + getwidth(); + + if(sb) + seekback(server_in, sb); + + while(read(server_in, buf, 1) > 0) + if(*buf == '\n') + break; + + if(victim && cistrncmp(victim, "MSGS", 4)){ + setwintitle(victim); + fprint(server_out, "JOIN %s\r\n", victim); + } + scr = 1; + + server_in = follow(server_in); + + if (charset != nil && strcmp(charset, "utf")) { + server_out = wtcs(server_out, charset); + server_in = rtcs(server_in, charset); + } + + if(replay) { + replayfile(); + } else { + if ((uipid = rfork(RFPROC|RFFDG|RFMEM)) == 0) + srvin(); + + usrin(); + + postnote(PNPROC, uipid, "kill"); + while (waitpid() != uipid); + } + + exits(0); +} + +int +wtcs(int fd, char *cset) +{ + int totcs[2]; + int pid; + + pipe(totcs); + + pid = fork(); + + if (pid == 0) { + dup(totcs[0], 0); + dup(fd, 1); + close(totcs[1]); + execl("/bin/tcs", "tcs", "-f", "utf", "-t", cset, nil); + exits("execfailure"); + } + close(totcs[0]); + + return totcs[1]; +} + +int +rtcs(int fd, char *cset) +{ + int fromtcs[2]; + int pid; + + pipe(fromtcs); + + pid = fork(); + + if (pid == 0){ + dup(fromtcs[1], 1); + dup(fd, 0); + close(fromtcs[0]); + execl("/bin/tcs", "tcs", "-f", cset, "-t", "utf", nil); + exits("execfailure"); + } + close(fromtcs[1]); + + return fromtcs[0]; +} + +int +follow(int fd) +{ + int p[2], pid; + long n; + char buf[1024]; + Dir *dp; + + pipe(p); + + pid = fork(); + if (pid == 0){ + dup(p[1], 1); + dup(fd, 0); + close(p[0]); + for(;;){ + while((n = read(0, buf, sizeof(buf))) > 0) + write(1, buf, n); + sleep(1000); + dp = dirfstat(0); + free(dp); + } + } + close(p[1]); + + return p[0]; +} + +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 *time, char *pre, char *, char *par[]) +{ + int n = 0; + char *buf; + char *c, *tc; + +/* + * if sent to victim, or comes from victim to non-channel, print. + * otherwise bail out. + */ + pre = prenick(pre); + if(victim) { + if((cistrncmp(victim, "MSGS", 4) == 0) && *par[0] != '#') { + /* catch-all for messages, fall through */ + + } else if(cistrcmp(par[0], victim)) + if(!pre || cistrcmp(pre, victim) || *par[0] == '#') + return 0; + } + + if(!pre) + buf = smprint("%s (%s) ⇐ %s\n", time, par[0], par[1]); + else if(*par[0] != '#') + buf = smprint("%s (%s) ⇒ %s\n", time, pre, par[1]); + else + buf = smprint("%s %s → %s\n", time, pre, par[1]); + + if(!buf) + sysfatal("failed to allocate space for message: %r\n"); + + c = buf; +again: + if(strlen(c) >= linewidth) { + for(tc = c + linewidth; tc > c; tc--) { + switch(*tc) { + case ' ': + *tc = '\0'; + n += fprint(scr, "%s\n", c); + c = tc+1; + goto again; + break; + default: + break; + } + } + } + n += fprint(scr, "%s", c); + free(buf); + return n; +} + +int +ntc(int, char *time, char *pre, char *, char *par[]) +{ + int n; + +/* + * if sent to victim, or comes from victim to non-channel, print. + * otherwise bail out. + */ + pre = prenick(pre); + if(victim && cistrcmp(par[0], victim)) + if(!pre || cistrcmp(pre, victim) || *par[0] == '#') + return 0; + + if(!pre) + n = fprint(scr, "%s [%s] ⇐\t%s\n", time, par[0], par[1]); + else if(*par[0] != '#') + n = fprint(scr, "%s [%s] ⇒\t%s\n", time, pre, par[1]); + else + n = fprint(scr, "%s [%s] %s →\t%s\n", time, par[0], pre, par[1]); + return n; +} + +int +generic(int, char *time, char *pre, char *cmd, char *par[]) +{ + int i = 0, r; + char *nick = prenick(pre); + +/* + * don't print crud on screens with victim set + */ + if(victim) + return 0; + + if (nick != nil) + r = fprint(scr, "%s %s (%s)\t", time, cmd, nick); + else + r = fprint(scr, "%s %s (%s)\t", time, cmd, par[i++]); + + for (; par[i] != nil; ++i) + r += fprint(scr, " %s", par[i]); + + r += fprint(scr, "\n"); + + return r; +} + +int +misc(int, char *time, char *pre, char *cmd, char *par[]) +{ + int i = 0, r; + char *nick = prenick(pre); + + if(cistrcmp(cmd,"QUIT")) + if(victim && par[0] && cistrcmp(par[0], victim)) + return 0; + + if (nick != nil) + r = fprint(scr, "%s %s (%s)\t", time, cmd, nick); + else + r = fprint(scr, "%s %s %s\t", time, cmd, par[i++]); + + for (; par[i] != nil; ++i) + r += fprint(scr, " %s", par[i]); + + r += fprint(scr, "\n"); + + return r; +} + +int +numeric(int, char *time, char *pre, char *cmd, char *par[]) +{ + int i = 0, r; + char *nick = prenick(pre); + + if(victim && par[1] && cistrcmp(par[1], victim)) + return 0; + + if (nick != nil) + r = fprint(scr, "%s %s (%s)\t", time, cmd, nick); + else + r = fprint(scr, "%s %s (%s)\t", time, cmd, par[i++]); + + for (; par[i] != nil; ++i) + r += fprint(scr, " %s", par[i]); + + r += fprint(scr, "\n"); + + return r; +} + +int +usrparse(char *ln, char *cmd, char *par[], int npar) +{ + enum state st = Cmd; + int i; + + for(i = 0; i < npar; i++) + par[i] = nil; + + if (ln[0] == '/' && npar >= 2) { + *cmd = ln[1]; + for (i = 1; ln[i] != '\0'; ++i) { + switch(st) { + case Cmd: + if (ln[i] == ' ') { + ln[i] = '\0'; + par[0] = ln+i+1; + st = Middle; + } else if(ln[i] == '\n') { + /* enable commands with no arguments */ + ln[i] = '\0'; + par[0] = nil; + par[1] = nil; + st = Ok; + } + break; + case Middle: + if (ln[i] == '\r' || ln[i] == '\n') { + ln[i] = '\0'; + st = Ok; + } + if (ln[i] == ' ') { + ln[i] = '\0'; + par[1] = ln+i+1; + st = Trail; + } + break; + case Trail: + if (ln[i] == '\r' || ln[i] == '\n') { + ln[i] = '\0'; + st = Ok; + } + break; + case Ok: + if (ln[i] == '\r' || ln[i] == '\n') + ln[i] = '\0'; + break; + } + } + } else { /* send line to victim by default */ + st = Ok; + *cmd = 'm'; + for (i = 0; ln[i] != '\0'; ++i) + if (ln[i] == '\r' || ln[i] == '\n') + ln[i] = '\0'; + par[0] = victim; + par[1] = ln; + } + 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; +} + +void +getwidth(void) +{ + Font *font; + int n, fd, mintab; + char buf[128], *f[10], *p; + + if(inacme){ + if((fd = open("/dev/acme/ctl", OREAD)) < 0) + return; + n = read(fd, buf, sizeof buf-1); + close(fd); + if(n <= 0) + return; + buf[n] = 0; + n = tokenize(buf, f, nelem(f)); + if(n < 7) + return; + if((font = openfont(nil, f[6])) == nil) + return; + mintab = stringwidth(font, "0"); + linewidth = atoi(f[5]); + linewidth = linewidth/mintab; + return; + } + + if((p = getenv("font")) == nil) + return; + if((font = openfont(nil, p)) == nil) + return; + if((fd = open("/dev/window", OREAD)) < 0) + return; + + n = read(fd, buf, 5*12); + close(fd); + + if(n < 5*12) + return; + + buf[n] = 0; + + /* window stucture: + 4 bit left edge + 1 bit gap + 12 bit scrollbar + 4 bit gap + text + 4 bit right edge + */ + linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4); + mintab = stringwidth(font, "0"); + linewidth = linewidth/mintab; +} diff --git a/install/irc7/irc.h b/install/irc7/irc.h new file mode 100644 index 0000000..4381ca2 --- /dev/null +++ b/install/irc7/irc.h @@ -0,0 +1,36 @@ +enum { + Pmsg, /* private message */ + Smsg, /* server message */ + Nmsg, /* notice */ + Lmsg, /* message sent by the client to server */ + Cmd, /* some other event such as a quit/join */ + Err = -1; +}; + +typedef struct Line Line; +struct Line +{ + int type; + char *from; /* who sent the message, can be nil for server messages */ + char *uhost; /* host where the message came from */ + int mid; /* message id for server messages + char *to; /* target for the message */ + char *cmd; /* JOIN/QUIT, etc. may be nil */ + char *text; /* message text */ +}; +#pragma varargck type "L" Line* + +void setwintitle(char *chan); + +int rtcs(int fd, char *cset); +int wtcs(int fd, char *cset); +int follow(int fd); + +int pmsg(int fd, char *pre, char *cmd, char *par[]); +int ntc(int fd, char *pre, char *cmd, char *par[]); +int generic(int fd, char *pre, char *cmd, char *par[]); +int misc(int fd, char *pre, char *cmd, char *par[]); +int numeric(int fd, char *pre, char *cmd, char *par[]); + +#define dprint if(debug) print + diff --git a/install/irc7/irc.man b/install/irc7/irc.man new file mode 100644 index 0000000..3b86d03 --- /dev/null +++ b/install/irc7/irc.man @@ -0,0 +1,131 @@ +.TH IRC 1 +.SH NAME +ircsrv, irc \- internet relay chat client and ui +.SH SYNOPSIS +.B ircsrv +[ +.I -s serv +] +[ +.I -f file +] +.I nickname +.I [tcp!]irc.server.org +.br +.B irc +[ +.I -e +] +[ +.I -c charset +] +[ +.I -t victim +] +[ +.I -b lines +] +[ +.I -r file +] +[ +.I -p password +] +[ +.I /srv/serv +[ +.I /tmp/file +]] +.SH DESCRIPTION +.I Ircsrv +makes a connection to an irc server and reconnects if the connection +gets broken. It posts a service to /srv and creates (if necessary) an +append mode log file to /tmp. Default file and service name is a +concatenation of 4 first letters of username and the string +.I irc +. +.PP +.I Irc +is a client program that makes use of the service provided by +.I ircsrv +and provides a user interface for sending and receiving +messages. +.PP +The command language understood by +.I irc +is as follows +.EX +/h this message +/H this message +/m privmsg #chan/nick message +/M mode #chan +nt +/j join [ #chan ] +/p part [ #chan ] +/q send parameters raw to the server +/l list [ #chan ] +/n nick newnick +/N notice #chan/nick message +/t set [ victim ] +/T topic [ #chan [ newtopic ]] +/W whois nick +/w who nick (a shorter whois) +.EE +.PP +The +.I -e +flag enables SSL, the +.I -c +flag sets character set conversion using +.IR tcs (1), +the +.I -r +flag will cause irc to replay the conversation from the raw file supplied as argument without user interaction, +the +.I -b +flag causes the client to display the last n lines of the default target or +conversations if no target is set. Without any arguments +.I -b +prints the conversation from the very beginning. +The +.I -t +flag sets the default target to which the messages are +sent by default if a line written to +.I irc +does not begin with /. Irc also prints messages to/from +the target exclusively if one is set. Irc prints only private messages if the target is set to "MSGS", useful for being notified when a message is sent to your nick without having to monitor all the status information. +.SH EXAMPLES +To start up ircsrv on a remote cpu server +.EX +cpu -c ircsrv glenda irc.freenode.net +.EE +.PP +To use ircsrv started earlier on a cpu server +.EX +import $cpu /srv +import $cpu /tmp +irc +.EE +.PP +To start/see/continue a conversation with a specific target (can be #chan or nick) +.EX +irc -t \'#plan9\' +irc -t f2f +.EE +.PP +To run irc within acme start irc inside +"win -e": +.EX +win -e +irc -t f2f +.EE +.SH SOURCE +/sys/src/cmd/ircsrv.c +.br +/sys/src/cmd/irc.c +.SH SEE ALSO +.IR tcs (1), +.IR faces (1), +.IR nedmail (1) +.SH BUGS +Probably. Missing functionality includes opening new windows when a +message from a new target arrives. diff --git a/install/irc7/ircsrv.6 b/install/irc7/ircsrv.6 new file mode 100644 index 0000000..5f06eed Binary files /dev/null and b/install/irc7/ircsrv.6 differ diff --git a/install/irc7/ircsrv.c b/install/irc7/ircsrv.c new file mode 100644 index 0000000..0976d56 --- /dev/null +++ b/install/irc7/ircsrv.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include + +char *post; +char *file; +int ircfd = -1; // the irc server +int logfd; +int enctls = 0; // ssl/tls +QLock lck; + +char *server; +char *passwd; +char *nickname; +char *realname; +char *username; +char *mode = "foo"; +char *unused = "bar"; + +void ircsrv(void); +void logger(void); +void die(void*, char*); +void reconnect(void); + +void +usage(void) +{ + fprint(2, "usage: %s [-e] [-s service] [-f file] [-p pass] nickname [net!]ircserver[!port]\n", argv0); + exits("usage"); +} + + +void +killall(void) +{ + postnote(PNGROUP, getpid(), "quit"); + while(waitpid() != -1) + ; + remove(post); + exits(nil); +} + +void +die(void *, char *) +{ + killall(); +} + +void +main(int argc, char *argv[]) +{ + char *tmp; + int p[2], fd; + + ARGBEGIN{ + case 'f': + file = EARGF(usage()); + break; + case 's': + post = EARGF(usage()); + break; + case 'r': + realname = EARGF(usage()); + break; + case 'e': + enctls = 1; + break; + case 'p': + passwd = EARGF(usage()); + /* try to obfuscate the password so ps -a won't see it */ + tmp = passwd; + passwd = smprint("%s", tmp); + if(passwd) + memset(tmp, '\0', strlen(tmp)); + else + passwd = tmp; + break; + default: + usage(); + }ARGEND; + + if(argc < 2) + usage(); + + + nickname = argv[0]; + server = argv[1]; + + username = getuser(); + + if(strlen(username) > 4) + username[4] = '\0'; + + + if(post == nil) + post = smprint("/srv/%sirc", username); + else + post = smprint("/srv/%s", post); + + if(file == nil) + file = smprint("/tmp/%sirc", username); + + if((logfd = create(file, OWRITE, 0600 | DMAPPEND)) < 0) + sysfatal("create(%s): %r", file); + + if((fd = create(post, OWRITE, 0600)) < 0) + sysfatal("create(%s): %r", post); + if(pipe(p) == -1) + sysfatal("pipe: %r"); + fprint(fd, "%d", p[1]); + close(fd); + close(p[1]); + close(0); + close(1); + close(2); + dup(p[0], 0); + + if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) { + notify(die); + reconnect(); + switch(rfork(RFPROC|RFMEM)){ + case -1: + sysfatal("rfork: %r"); + case 0: + notify(die); + logger(); + break; + default: + ircsrv(); + break; + } + } + exits(nil); +} + +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 +reregister(void) +{ + int n; + char nbuf[32]; + + strncpy(nbuf, nickname, sizeof(nbuf) - 2); + switch(nbuf[strlen(nbuf) - 1]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + nbuf[strlen(nbuf) - 1]++; + break; + case '9': + qlock(&lck); + fprint(logfd, "%ld can not register nick, bailing out\n", time(0)); + qunlock(&lck); + die(nil, nil); + default: + n = strlen(nbuf); + nbuf[n] = '0'; + nbuf[n+1] = '\0'; + break; + } + qlock(&lck); + fprint(ircfd, "NICK %s\r\n", nbuf); + fprint(logfd, "%ld NICK %s\r\n", time(0), nickname); + qunlock(&lck); +} + +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"); } + } + if(passwd && strcmp(passwd, "")) + fprint(ircfd, "PASS %s\r\n", passwd); + fprint(ircfd, "USER %s %s %s :%s\r\n", + nickname, mode, unused, realname); + fprint(ircfd, "NICK %s\r\n", nickname); +} + + +void +logger(void) +{ + char buf[513]; + char *f[3]; + long n; + + for(;;){ + while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){ + fprint(logfd, "%ld ", time(0)); + write(logfd, buf, n); + buf[n] = 0; + n = tokenize(buf, f, nelem(f)); + if(n == 3 && *f[0] == ':' && !cistrcmp(f[1], "PING")){ + qlock(&lck); + fprint(ircfd, "PONG %s\r\n", f[2]); + fprint(logfd, "%ld PONG %s\r\n", time(0), f[2]); + qunlock(&lck); + } else if(n == 2 && !cistrcmp(f[0], "PING")){ + qlock(&lck); + fprint(ircfd, "PONG %s\r\n", f[1]); + fprint(logfd, "%ld PONG %s\r\n", time(0), f[1]); + qunlock(&lck); + } else if(n == 3 && atoi(f[1]) == 433) { + reregister(); + } + } + reconnect(); + } +} + +void +ircsrv(void) +{ + char buf[512]; + long n; + + while((n = readln(0, buf, sizeof(buf)-1)) > 0){ + qlock(&lck); + fprint(logfd, "%ld ", time(0)); + if(write(logfd, buf, n) != n) + fprint(2, "write to irclog: %r\n"); + if(write(ircfd, buf, n) != n) + fprint(2, "write to ircserver: %r\n"); + qunlock(&lck); + } + killall(); +} + + diff --git a/install/irc7/mkfile b/install/irc7/mkfile new file mode 100644 index 0000000..c6b09d4 --- /dev/null +++ b/install/irc7/mkfile @@ -0,0 +1,15 @@ +