ref: 50bc9f2c47441facf926715dca4e73da381d6a5d
dir: /irc/irc.c/
#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;
}