shithub: svc

ref: 063d86a3015f5f254389e61b57e7f14822e889c3
dir: /irc/irc.c/

View raw version
#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;
}