added neindaw script

This commit is contained in:
glenda 2020-12-17 21:13:41 +00:00
parent 907105032b
commit dd77de7a50
27 changed files with 1311 additions and 0 deletions

View File

@ -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

Binary file not shown.

1
install/irc7/.hg/branch Normal file
View File

@ -0,0 +1 @@
default

View File

@ -0,0 +1,2 @@
ac16a1e78cdfc12ffd5af66949e50846e580c5e5 1
ac16a1e78cdfc12ffd5af66949e50846e580c5e5 default

BIN
install/irc7/.hg/dirstate Normal file

Binary file not shown.

2
install/irc7/.hg/hgrc Normal file
View File

@ -0,0 +1,2 @@
[paths]
default = https://code.9front.org/hg/irc7

View File

@ -0,0 +1,3 @@
revlogv1
store
fncache

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
data/irc.c.i
data/irc.h.i
data/irc.man.i
data/ircsrv.c.i
data/mkfile.i

BIN
install/irc7/.hg/store/undo Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
default

View File

BIN
install/irc7/6.irc Executable file

Binary file not shown.

BIN
install/irc7/6.ircsrv Executable file

Binary file not shown.

BIN
install/irc7/irc.6 Normal file

Binary file not shown.

854
install/irc7/irc.c Normal file
View File

@ -0,0 +1,854 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
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;
}

36
install/irc7/irc.h Normal file
View File

@ -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

131
install/irc7/irc.man Normal file
View File

@ -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.

BIN
install/irc7/ircsrv.6 Normal file

Binary file not shown.

255
install/irc7/ircsrv.c Normal file
View File

@ -0,0 +1,255 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <libsec.h>
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();
}

15
install/irc7/mkfile Normal file
View File

@ -0,0 +1,15 @@
</$objtype/mkfile
MAN=/sys/man/1
TARG=\
irc\
ircsrv\
BIN=/$objtype/bin
</sys/src/cmd/mkmany
ircman:V:
cp irc.man /sys/man/1/irc
install: ircman