shithub: ircd

ref: c86384b841687ea975571db934fb0e353ce5b5d4
dir: /cmd.c/

View raw version
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
#include "cmd.h"
#include "ll.h"
#include "version.h"

static char channelprefix[] = "&#+!";

typedef struct AuxArgs AuxArgs;
struct AuxArgs {
	IChan *channel;
	Client *client;
	char *str;
};

static void
cversion(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.4.3) */
	if (r->args[0]) {
		fprint(2, "get version of '%s' (not implemented yet!)\n", r->args[0]);
		reply(c, Enosuchserver, r->args[0]);
		return;
	}
	reply(c, Rversion, getversion());
}

static void
postconnect(Client *c)
{
	reply(c, Rwelcome, c);
	reply(c, Ryourhost, sysnameb, Vversion);
	reply(c, Rcreated, "sometime"); // TODO
	reply(c, Rmyinfo, sysnameb, Vversion, "i", "ov");
	reply(c, Rmotdstart, sysnameb);
	reply(c, Rmotd);
	reply(c, Rmotdend);
}

static void
cuser(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.1.3) */
	User *u;
	if (!r->args[3]) {
		reply(c, Eneedmoreparams, r->cmd->name);
		return;
	}
	u = finduser(r->args[0]);
	if (u) {
		reply(c, Ealreadyregistered);
		return;
	}
	u = adduser(r->args[0]);
	u->realname = strdup(r->args[3]);
	u->host = strdup(sysnameb);
	c->nick = strdup(r->args[0]);
	c->user = u;
	postconnect(c);
}

static void
cnick(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.1.2) */
	User *u;
	if (!r->args[0]) {
		reply(c, Enonicknamegiven);
		return;
	}
	
	if (findnick(r->args[0])) {
		reply(c, Enicknameinuse, r->args[0]);
		return;
	}
	
	c->nick = strdup(r->args[0]);
	u = finduser(r->args[0]);
	if (u) {
		reply(c, Enicknameinuse, r->args[0]);
		return;
	}
	u = adduser(r->args[0]);
	u->host = sysnameb;
	c->user = u;
	
	postconnect(c);
}

static void
cwhois(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.6.2) */
	// TODO: implement remaining replies: (/lib/rfc/rfc2812:/^3.6.2)
	int start = 0;
	char *server = nil;
	User *u;
	Client *cl;
	
	if (!r->args[0]) {
		reply(c, Enonicknamegiven);
		return;
	}
	if (r->args[1] && strcmp(r->args[1], ",")) {
		server = r->args[0];
		start = 2;
	}
	for (int i = start; i < 15; i += 2) {
		/* args[i] is nick, args[i+1] is ',' */
		if (!r->args[i])
			break;
		if (!server) {
			cl = findnick(r->args[i]);
			if (cl) {
				u = cl->user;
				if (!u)
					goto Next;
				reply(c, Rwhoisuser, cl->nick, u->name, sysnameb, u->realname);
			} else
				reply(c, Enosuchnick, r->args[i]);
		} else {
			// TODO: forward to server: send message to server
		}
Next:
		reply(c, Rendofwhois, r->args[i]);
	}
}

typedef struct A_privmsgsend A_privmsgsend;
struct A_privmsgsend {
	char *msg;
	IChan *chan;
	Client *sender;
};

static void
acprivmsgsend(void *a, void *b)
{
	Client *c = a;
	AuxArgs *args = b;
	
	if (c == args->client)
		return;
	ircsend(c, args->client, Sprivmsg, args->channel->name, args->str);
}

static void
cprivmsg(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.3.1) */
	Client *tgt;
	IChan *chan;
	AuxArgs args;
	
	if (!r->args[0] || r->args[0][0] == 0) {
		reply(c, Enorecipient, "PRIVMSG");
		return;
	}
	if (!r->args[1] || r->args[1][0] == 0) {
		reply(c, Enotexttosend);
		return;
	}
	if (strchr(channelprefix, r->args[0][0])) {
		/* target is channel */
		chan = findchannel(r->args[0]);
		if (!(chan && userinchannel(chan, c))) {
			reply(c, Ecannotsendtochan, r->args[0]);
			return;
		}
		args.channel = chan;
		args.str = r->args[1];
		args.client = c;
		lforeach(&chan->users, acprivmsgsend, &args);
		return;
	}
	/* target is user */
	tgt = findnick(r->args[0]);
	if (!tgt) {
		reply(c, Enosuchnick, r->args[0]);
		return;
	}
	if (tgt->away) {
		reply(c, Raway, r->args[0], tgt->away);
		return;
	}
	ircsend(tgt, c, Sprivmsg, r->args[0], r->args[1]);
}

static void
caway(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^4.1) */
	if (r->args[0]) {
		if (c->away)
			free(c->away);
		c->away = strdup(r->args[0]);
		reply(c, Rnowaway);
		return;
	}
	if (c->away)
		free(c->away);
	c->away = nil;
	reply(c, Runaway);
}

/* Aux Cmd function */
static void
aclistnames(void *a, void *b)
{
	Client *c = a;
	AuxArgs *ln = b;
	
	reply(ln->client, Rnamreply, '=', ln->channel->name, c->nick);
}

static void
acsendjoin(void *cl, void *aux)
{
	Client *c = cl;
	AuxArgs *a = aux;
	ircsend(c, a->client, Sjoin, a->channel->name);
}

static void
cjoin(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.2.1) */
	char *channel;
	IChan *ch;
	AuxArgs ln;
	
	char *chs[10];
	int nch;
	
	if (!r->args[0] || r->args[0][0] == 0) {
		goto Err;
	}
	
	nch = getfields(r->args[0], chs, sizeof(chs), 1, ",");
	if (!nch) {
		goto Err;
	}
	
	for (int i = 0; i < nch; i++) {
		channel = chs[i];
		ch = joinchannel(c, channel);
		ircsend(c, nil, Sjoin, channel);
		
		if (ch->topic)
			reply(c, Rtopic, channel, ch->topic);
		else
			reply(c, Rnotopic, channel);
		
		ln.client = c;
		ln.channel = ch;
		lforeach(&ch->users, aclistnames, &ln);
		reply(c, Rendofnames, channel);
		lforeach(&ch->users, acsendjoin, &ln);
	}
	return;
Err:
	reply(c, Eneedmoreparams, "JOIN");
}

static void
acpart(void *a, void *b)
{
	Client *c = a;
	AuxArgs *args = b;
	
	ircsend(c, args->client, Spart, args->str ? args->str : args->client->nick);
}

static void
cpart(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.2.2) */
	int i;
	char *msg;
	char *chs[10];
	IChan *ch;
	int nch;
	AuxArgs args;
	
	if (!r->args[0] || r->args[0][0] == 0) {
		goto Err;
	}
	
	msg = nil;
	if (r->args[1] && r->args[1][0] != 0)
		msg = r->args[1];
	
	nch = getfields(r->args[0], chs, sizeof(chs), 1, ",");
	if (!nch) {
		goto Err;
	}
	
	for (i = 0; i < nch; i++) {
		ch = findchannel(chs[i]);
		if (!ch) {
			reply(c, Enosuchchannel, chs[i]);
		} else {
			if (userinchannel(ch, c)) {
				args.channel = ch;
				args.client = c;
				args.str = msg;
				lforeach(&ch->users, acpart, &args);
				partchannel(ch, c);
			} else {
				reply(c, Enotonchannel, chs[i]);
			}
		}
	}
	return;
Err:
	reply(c, Eneedmoreparams, "PART");
}

static void
cquit(Client *c, Request *r)
{
	/* (/lib/rfc/rfc2812:/^3.1.7) */
	USED(r);
	ircerror(c, "Bye");
	if (c->user)
		deluser(c->user);
	if (c->nick)
		free(c->nick);
	if (c->away)
		free(c->away);
	c->nick = c->away = nil;
	c->user = nil;
}

static Command commands[] = {
	{ "whois", cwhois },
	{ "version", cversion },
	{ "user", cuser },
	{ "nick", cnick },
	{ "privmsg", cprivmsg },
	{ "away", caway },
	{ "join", cjoin },
	{ "part", cpart },
	{ "quit", cquit },
};
int ncommands = sizeof(commands) / sizeof(Command);

Command*
findcommand(char *s)
{
	for (int i = 0; i < ncommands; i++) {
		if (cistrcmp(commands[i].name, s) == 0)
			return &commands[i];
	}
	return nil;
}

void
execrequest(Client *c, Request r)
{
	if (!(r.cmd && r.cmd->func)) {
		fprint(2, "cannot execute request: no command\n");
		return;
	}
	if (debug)
		fprint(2, "run command '%s'\n", r.cmd->name);
	r.cmd->func(c, &r);
}