ref: 2716869c69ae007306e1b2bacfc0bf7adbccb0fc
dir: /cmd.c/
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
#include "cmd.h"
#include "ll.h"
#include "version.h"
static char channelprefix[] = "&#+!";
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;
A_privmsgsend *args = b;
if (c == args->sender)
return;
ircsend(c, args->sender, Sprivmsg, args->chan->name, args->msg);
}
static void
cprivmsg(Client *c, Request *r)
{
/* (/lib/rfc/rfc2812:/^3.3.1) */
Client *tgt;
IChan *chan;
A_privmsgsend pmsg;
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;
}
pmsg.chan = chan;
pmsg.msg = r->args[1];
pmsg.sender = c;
lforeach(&chan->users, acprivmsgsend, &pmsg);
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);
}
typedef struct A_listnames A_listnames;
struct A_listnames {
IChan *channel;
Client *client;
};
/* Aux Cmd function */
static void
aclistnames(void *a, void *b)
{
Client *c = a;
A_listnames *ln = b;
reply(ln->client, Rnamreply, '=', ln->channel->name, c->nick);
ircsend(c, ln->client, Sjoin, ln->channel->name);
}
static void
cjoin(Client *c, Request *r)
{
/* (/lib/rfc/rfc2812:/^3.2.1) */
char *channel;
IChan *ch;
A_listnames ln;
if (!r->args[0] || r->args[0][0] == 0) {
reply(c, Eneedmoreparams, "JOIN");
return;
}
for (int i = 0; i < 15;) {
/* i is channel, i+1 is ',' */
channel = r->args[i];
if (!channel)
break;
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);
if (r->args[i+1]) {
if (!strcmp(r->args[i+1], ",")) {
i += 2;
continue;
} else {
// TODO: syntax error?
}
} else
break;
}
}
static void
cquit(Client *c, Request *r)
{
/* (/lib/rfc/rfc2812:/^3.1.7) */
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 },
{ "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;
}
Command*
findcommandn(int n)
{
assert(0);
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);
}