/*
* commands.c: all the client commands.
*
* Copyright(c) 1997-2000 - All Rights Reserved
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: commands.c,v 1.66 2001/03/14 19:50:20 kalt Exp $";
#endif
#include "os.h"
#include "struct.h"
#include "server.h"
#include "window.h"
#include "format.h"
#include "option.h"
#include "term.h"
#include "utils.h"
#include "input.h"
extern struct server_ *server;
extern char tab_add(char *, char *);
extern char space_add(char *, char *);
extern int cmd_ctcp(char *);
extern int cmd_ping(char *);
extern int cmd_seen(char *);
extern int cmd_lastlog(char *);
extern int sic_wreformat();
extern char *yow();
void parse_command(char *, int);
struct cmdlist_
{
char *name;
int (*func) (char *);
};
struct aliaslist_
{
char *name;
char *eval;
struct aliaslist_ *nexta;
};
static struct aliaslist_ *alias = NULL;
/* return codes:
* 0 "success"
* -1 "need more parameters"
* -2 "no recipient"
* -3 "no text to send"
* -4 "bad params"
* -5 "bind window to server first"
* -9 "not implemented"
*/
static char outp[1024];
static u_char outa[1024];
/* cmd_smart: if the first word in the buffer is not a channel, prepend
* the window default channel name to the buffer.
*/
static char *
cmd_smart(ibuf)
char *ibuf;
{
static char newbuf[1024];
struct channel_ *ctmp;
char *sp;
if (*ibuf == '&' || *ibuf == '#' || *ibuf == '+')
{
char known;
if (sp = index(ibuf, ' '))
*sp = '\0';
known = map_c2w(ibuf);
if (sp)
*sp = ' ';
if (known)
return ibuf;
}
ctmp = get_channel(NULL);
if (ctmp == NULL)
return "";
sprintf(newbuf, "%s%s%s", ctmp->chname, (*ibuf) ? " " : "", ibuf);
return newbuf;
}
/*
* IRC commands
*/
static int
cmd_epart(p, forget)
char *p;
char forget;
{
char *c;
p = cmd_smart(p);
c = index(p, ' ');
if (!*p)
return -1;
if (c)
*c++ = '\0';
if (map_c2w(p) == 0)
{
vsic_slog(LOG_CLIENT, "--- Channel %s is unknown to me", p);
return 0;
}
part_channel(p, (c) ? c : "", forget);
return 0;
}
static int
cmd_cmode(p)
char *p;
{
p = cmd_smart(p);
if (!*p)
return -1;
vsic_write("MODE %s", p);
return 0;
}
static int
cmd_join(p)
char *p;
{
char *key = index(p, ' ');
struct channel_ *ctmp;
if (!*p)
return -1;
if (index(p, ','))
return -9; /* better this than a crash */
if (server == NULL)
return -5;
if (key)
*key++ = '\0';
switch (map_c2w(p))
{
case 0 :
new_channel(p);
break;
case 1 :
break;
case 2 :
vsic_slog(LOG_CLIENT,
"--- Channel %s was mapped to a different window", p);
sic_wchannel(p);
break;
default :
abort(); /* never */
}
ctmp = get_channel(p);
if (key && *key)
{
if (ctmp->chkey)
free(ctmp->chkey);
ctmp->chkey = strdup(key);
}
join_channel(p, 0);
return 0;
}
static int
cmd_kick(p)
char *p;
{
char *comment;
p = cmd_smart(p);
if (!*p)
return -1;
if (comment = index(p, ' '))
if (comment = index(comment+1, ' '))
*comment++ = '\0';
vsic_write("KICK %s :%s", p, (comment) ? comment : "");
return 0;
}
static int
cmd_leave(p)
{
return cmd_epart(p, 0);
}
static int
cmd_list(p)
char *p;
{
vsic_write("LIST %s", (*p) ? p : cmd_smart(p));
return 0;
}
int
cmd_msgnotice(cmd, p)
char *cmd, *p;
{
char *txt = index(p, ' '), fmt;
int rendering;
unsigned int flags = 0;
if (!*p)
return -2;
if (cmd)
{
if (!txt || !*(txt+1))
return -3;
*txt++ = '\0';
if (rmatch("*@[*]", p))
{
char *s = index(index(p, '@'), ']');
*s = '\0';
s = index(p, '@');
*s++ = '\0';
s += 1;
if (select_server(s) == NULL)
{
vsic_slog(LOG_CLIENT, "--- No connected to %s.", s);
return 0;
}
}
if (*p != '&' && *p != '#' && *p != '+' && *txt != '\001')
tab_add(p, (*cmd == 'S') ? NULL : "");
if (*p == '=')
if (*cmd == 'N')
return -4;
else
{
p++;
cmd = "DCC";
}
}
else
{
cmd = "PRIVMSG";
txt = p;
if (p = get_query())
{
if (*p == '=')
{
cmd = "DCC";
p++;
}
else if (index(p, '@'))
cmd = "SQUERY";
}
else
{
if (get_channel(NULL) == NULL)
return -3;
p = get_channel(NULL)->chname;
}
}
if (*p != '=' && server == NULL)
return -5;
fmt = get_channel(p) ? (*cmd == 'P') ? F_MYPUBM : F_MYPUBN :
(*cmd == 'D') ? F_MYDCC : (*cmd == 'P') ? F_MYPRIVM : F_MYPRIVN;
rendering = out_sprintf(outp, outa, get_format(fmt, p), p, cmd, txt);
select_active(get_channel(p) ? p : NULL, 0);
if (*cmd == 'P' && get_channel(p) == NULL)
{
char tmpq[512];
sprintf(tmpq, "%s%s", (*cmd == 'D') ? "=" : "", p);
select_query(tmpq);
}
flags = LOG_CLIENT;
flags |= (*cmd == 'D') ? LOG_DCC : (*cmd == 'P') ? LOG_MSG : LOG_NOTICE;
flags |= get_channel(p) ? LOG_PUBLIC : LOG_PRIVATE;
if (*cmd == 'N' && *txt == '\001'
&& !get_option(Z_VCTCP, (get_channel(p)) ? p : NULL))
flags |= LOG_HIDE;
sic_log(flags, 0, NULL, cmd, p, txt, fmt, outp, rendering ? outa : NULL);
if (*cmd == 'D')
vsic_dwrite(p, "%s", txt);
else
vsic_write("%s %s :%s", cmd, p, txt);
if (get_channel(p) && (p = index(txt, ':')) < index(txt, ' ') && p)
{
*p = '\0';
space_add(txt, "");
}
return 0;
}
static int
cmd_msg(p)
char *p;
{
return cmd_msgnotice("PRIVMSG", p);
}
static int
cmd_names(p)
char *p;
{
vsic_write("NAMES %s", (*p) ? p : cmd_smart(p));
return 0;
}
static int
cmd_notice(p)
char *p;
{
return cmd_msgnotice("NOTICE", p);
}
static int
cmd_part(p)
char *p;
{
return cmd_epart(p, 1);
}
static int
cmd_quit(p)
char *p;
{
vsic_write("QUIT :%s", p);
cmd_server(NULL);
return 0;
}
static int
cmd_squery(p)
char *p;
{
char *txt = index(p, ' ');
int rendering;
if (!*p)
return -2;
if (server == NULL)
return -5;
if (!txt || !*(txt+1))
return -3;
*txt++ = '\0';
vsic_write("SQUERY %s :%s", p, txt);
rendering = out_sprintf(outp, outa, get_format(F_MYQUERY, NULL),
p, "SQUERY", txt);
select_active(NULL, 0);
sic_log(LOG_CLIENT|LOG_PRIVATE|LOG_MSG,
0, NULL, "SQUERY", p, txt, F_MYQUERY, outp, rendering ? outa : NULL);
return 0;
}
static int
cmd_topic(p)
char *p;
{
char *comment;
if (!*p)
p = cmd_smart(p);
if (!*p)
return -1;
if (comment = index(p, ' '))
*comment++ = '\0';
vsic_write("TOPIC %s%c%s", p, (comment) ? ':' : ' ',
(comment) ? comment : "");
return 0;
}
static int
cmd_umode(p)
char *p;
{
if (server == NULL)
return -5;
vsic_write("MODE %s %s", server->nick, p);
return 0;
}
static int
cmd_who(p)
char *p;
{
vsic_write("WHO %s", (*p) ? p : cmd_smart(p));
return 0;
}
/*
* internal commands
*/
int
cmd_alias(p)
char *p;
{
char tmp[1024], *wp = tmp, *txt;
struct aliaslist_ *atmp = alias;
if (txt = index(p, ' '))
{
*txt++ = '\0';
while (atmp)
{
if (!strcasecmp(p, atmp->name))
break;
atmp = atmp->nexta;
}
if (atmp)
free(atmp->eval);
else
{
atmp = (struct aliaslist_ *) malloc(sizeof(struct aliaslist_));
atmp->name = strdup(p);
atmp->nexta = alias;
alias = atmp;
}
while (*txt)
{
if (*txt == '%' && isdigit((int) *(txt+1)))
{
*wp++ = *txt++; /* % */
*wp++ = '{';
while (isdigit((int) *txt))
*wp++ = *txt++;
*wp++ = '}';
}
else
*wp++ = *txt++;
}
*wp = '\0';
atmp->eval = strdup(tmp);
}
else
{
int len = strlen(p);
sic_slog(LOG_CLIENT, "--- Aliases:");
while (atmp)
{
if (!strncasecmp(p, atmp->name, len))
vsic_slog(LOG_CLIENT, "--- %-8s: %s", atmp->name,atmp->eval);
atmp = atmp->nexta;
}
}
return 0;
}
static int
cmd_clear(p)
char *p;
{
if (*p && !strcasecmp(p, "-f"))
sic_clog(1);
else
sic_clog(0);
sic_scroll2(+1, NULL);
sic_redowin(0);
return 0;
}
static int
cmd_exit(p)
char *p;
{
sic_slog(LOG_CLIENT, "Exiting...");
term_end();
exit(0);
}
static int
cmd_dns(p)
char *p;
{
if (*p)
{
vsic_slog(LOG_CLIENT, "--- Looking up \"%s\" in DNS", p);
dns_lookup(p, NULL);
return 0;
}
else
return -1;
}
int
cmd_option(p)
char *p;
{
static char *channel = NULL;
char *c, *v;
unsigned int flag, all = 0;
int fnum = -1;
if (!*p)
return -1;
if (channel)
{
free(channel);
channel = NULL;
}
if (v = index(p, ' '))
*v++ = '\0';
switch (*p)
{
case 'c': case 'C':
flag = O_CLEAR;
break;
case 'g': case 'G':
flag = O_GET;
break;
case 's': case 'S':
flag = O_SET;
break;
case 'r': case 'R':
sic_wreformat();
sic_redowin(1);
return 0;
default:
return -4;
}
if ((c = index(p, '.')) == NULL)
return -4;
switch (*(++c))
{
case 'a': case 'A':
all = 1;
if (flag != O_GET)
return -4;
break;
case 'c': case 'C':
if ((c = index(p, '=')) == NULL)
return -4;
if ((p = index(c, '.')) == NULL)
return -4;
c +=1 ;
*p = '\0';
if (c == p)
if (get_channel(NULL))
c = get_channel(NULL)->chname;
else
c = NULL;
if (c == NULL || map_c2w(c) == 0)
{
vsic_slog(LOG_CLIENT, "%s: No such channel", c);
return 0;
}
channel = strdup(c);
*(c = p) = '.';
break;
case 'd': case 'D':
if (flag != O_GET)
return -4;
flag |= O_DEFAULT;
break;
case 'p': case 'P':
flag |= O_PROTO;
break;
case 's': case 'S':
flag |= O_SERVER;
break;
case 't': case 'T':
flag |= O_TOPLVL;
break;
case 'w': case 'W':
flag |= O_WINDOW;
break;
default:
return -4;
}
if ((p = index(c, '.')) == NULL)
return -4;
if (c = index(++p, '.'))
*(c++) = '\0';
if (!strcasecmp(p, "on"))
flag |= O_ON;
else if (!strcasecmp(p, "off"))
flag |= O_OFF;
else if (*p == '#' && isdigit((int) *(p+1)))
fnum = atoi(p+1) + F_MAX+1;
else if (isdigit((int) *p))
fnum = atoi(p);
else if (*p == 'k' || *p == 'K')
flag |= O_KEYWORD;
else if (*p == 'l' || *p == 'L')
flag |= O_LOGFILE;
else if (*p == 's' || *p == 'S')
flag |= O_SWITCH;
else if (*p == 'r' || *p == 'R')
flag |= O_REWRITE;
else
return -4;
if (flag & (O_ON|O_OFF))
{
if (c == NULL)
return -4;
flag |= O_MASK;
}
if (flag & (O_GET|O_CLEAR))
{
unsigned char i;
char *str = NULL;
unsigned int all_list[] = {O_DEFAULT, O_TOPLVL, O_SERVER, O_WINDOW},
ui = 0;
for (i = 0; (i == 0 || all) && i < 4; i++)
{
if (flag & (O_MASK|O_SWITCH))
str = c;
if (flag & O_KEYWORD)
str = v;
if (flag & O_REWRITE)
{
ui = (c) ? atoi(c) : F_MAX+1;
str = v;
}
customize((all) ? flag|all_list[i] : flag, channel, fnum, &str, &ui);
if (flag & O_GET)
{
if (flag & (O_KEYWORD|O_REWRITE))
{
int count = fnum; /* -1 */
do
{
if (str)
if (flag & O_KEYWORD)
vsic_slog(LOG_CLIENT, "Keyword: %5.5X %s",
ui, str);
else
vsic_slog(LOG_CLIENT, "Rewrite: %3u %s",
ui, str);
str = v; fnum = --count;
if (flag & O_REWRITE)
ui = (c) ? atoi(c) : F_MAX+1;
customize((all) ? flag|all_list[i] : flag, channel,
fnum, &str, &ui);
}
while (str != NULL && v == NULL);
fnum = -1;
}
else if (flag & O_MASK)
{
char *str2 = c;
unsigned int ui2 = 0;
customize((all) ? (flag|all_list[i])^(O_ON|O_OFF) :
flag^(O_ON|O_OFF), channel, fnum, &str2, &ui2);
vsic_slog(LOG_CLIENT, "Set to: \"%8.8X\" - \"%8.8X\"",
ui, ui2);
}
else if (flag & O_LOGFILE)
vsic_slog(LOG_CLIENT, "Log: %s, Filter: %d", str, ui);
else if (str)
vsic_slog(LOG_CLIENT, "Set to: \"%c%s\"",
(*str) ? *str : ':', str+1);
else
vsic_slog(LOG_CLIENT, "Set to: %8.8X", ui);
}
}
return 0;
}
if (v)
{
unsigned int ui = 0;
char *str = NULL;
if (flag & O_SWITCH)
{
if (!strcasecmp(v, "on"))
ui = 1;
else
ui = 0;
str = c;
}
else
{
ui = (unsigned int) strtoul(v, &str, 0);
#if !defined(HAVE_REGEXP)
if (flag & O_REWRITE || ((flag & O_KEYWORD) && (ui & K_REGEXP)))
{
sic_slog(LOG_CLIENT,
"--- Option not available (requires regexp library).");
return 0;
}
#endif
if (flag & O_MASK)
str = c;
else if (flag & O_REWRITE)
ui = atoi(c);
else if (str)
while (*str && isspace((int) *str))
str++;
}
customize(flag, channel, fnum, &str, &ui);
if (flag & (O_LOGFILE|O_REWRITE|O_KEYWORD) && str)
vsic_slog(LOG_CLIENT, "--- Error: %s", str);
return 0;
}
else
return -4;
}
static int
cmd_query(p)
char *p;
{
char *txt = index(p, ' ');
if (txt)
*txt = '\0';
if (map_c2w(p) == 0)
{
default_channel(p);
if (txt && *(txt+1))
{
*txt = ' ';
cmd_msgnotice("PRIVMSG", p);
}
}
else
sic_slog(LOG_CLIENT,"--- Cannot query a channel!");
return 0;
}
static int
cmd_window(p)
char *p;
{
if (!*p)
return -1;
if (!strcasecmp(p, "new"))
sic_newwin();
else if (!strcasecmp(p, "next"))
sic_chgwin(1);
else if (!strcasecmp(p, "kill"))
sic_wkill();
else if (!strcasecmp(p, "last"))
sic_chgwin(-3);
else if (!strcasecmp(p, "list"))
sic_wlist();
else if (!strcasecmp(p, "previous"))
sic_chgwin(-2);
else if (!strcasecmp(p, "release"))
sic_swin(2);
else if (!strncasecmp(p, "number ", 7))
if (p = index(p, ' '))
if (atoi(++p) >= 0 && atoi(p) < 11)
sic_wch(atoi(p));
else
return -4;
else
return -1;
else if (!strncasecmp(p, "channel ", 8))
if (map_c2w(p+8) == 1)
default_channel(p+8);
else
sic_slog(LOG_CLIENT,"--- No such channel or channel not in window.");
else if (!strncasecmp(p, "dcc", 3))
sic_wdcc();
else
return -4;
return 0;
}
static int
cmd_yow(p)
char *p;
{
char *zippy;
zippy = yow();
if (zippy)
vsic_slog(LOG_CLIENT, "--- %s", zippy);
return 0;
}
struct cmdlist_ cmdlist[] = {
{ "ADMIN", NULL },
{ "ALIAS", cmd_alias},
{ "AWAY", NULL },
{ "CLOSE", NULL },
{ "CONNECT", NULL },
{ "CLEAR", cmd_clear},
{ "CMODE", cmd_cmode},
{ "CTCP", cmd_ctcp},
{ "DCC", cmd_dcc},
{ "DIE", NULL },
{ "DNS", cmd_dns},
{ "EXIT", cmd_exit},
{ "INFO", NULL },
{ "INVITE", NULL },
{ "ISON", NULL },
{ "JOIN", cmd_join },
{ "KICK", cmd_kick},
/*{ "KILL", NULL },*/
{ "LASTLOG", cmd_lastlog},
{ "LEAVE", cmd_leave},
{ "LINKS", NULL },
{ "LIST", cmd_list},
{ "LUSERS", NULL },
{ "MOTD", NULL },
{ "MODE", NULL },
{ "MSG", cmd_msg},
{ "NAMES", cmd_names},
{ "NICK", NULL },
{ "NOTICE", cmd_notice},
{ "OPER", NULL },
{ "OPTION", cmd_option},
{ "PART", cmd_part},
{ "PING", cmd_ping},
/* PING/PONG */
{ "QUERY", cmd_query},
{ "QUIT", cmd_quit},
{ "REHASH", NULL },
{ "RESTART", NULL },
{ "RUN", cmd_run},
{ "SEEN", cmd_seen},
{ "SERVER", cmd_server},
{ "SERVLIST", NULL },
{ "SQUERY", cmd_squery},
/*{ "SQUIT", NULL },*/
{ "STATS", NULL },
{ "TIME", NULL },
{ "TOPIC", cmd_topic },
{ "TRACE", NULL },
{ "UMODE", cmd_umode},
{ "USERHOST", NULL },
{ "VERSION", NULL },
{ "WINDOW", cmd_window },
{ "WHO", cmd_who },
{ "WHOIS", NULL },
{ "WHOWAS", NULL },
{ "YOW", cmd_yow },
{ NULL, NULL }
};
/* parse_alias: parse and execute an alias */
void
parse_alias(acmd, str)
struct aliaslist_ *acmd;
char *str;
{
static unsigned char recursion = 0;
char *slice = acmd->eval, *wp = acmd->eval, *rp;
char command[1024];
if (recursion++ >= 5)
{
vsic_slog(LOG_CLIENT, "--- Alias %s: maximum recursion exceeded.",
acmd->name);
return;
}
while (wp)
{
slice = index(slice+1, ';');
command[0] = '\0'; rp = command;
while(*wp && (!slice || wp < slice))
{
while(*wp && (!slice || wp < slice) && *wp != '%')
*rp++ = *wp++;
if (*wp && (!slice || wp < slice))
{
wp++; /* skip % */
*rp = '\0'; /* for strcat() to work */
switch (*wp++)
{
case '%':
*rp++ = '%'; *rp = '\0';
break;
case '*':
strcat(command, str);
break;
case '{':
strcat(command, sic_split(str, wp-1));
while (*wp != '}')
wp++;
wp++;
break;
}
while (*rp != '\0')
rp++;
}
}
*rp = '\0';
if (*command == '/')
parse_command(command+1, 1);
else
cmd_msgnotice(NULL, command);
wp = slice;
}
recursion--;
}
/* parse_command: parse an input string that is a command/alias. */
void
parse_command(str, aliases)
char *str;
int aliases;
{
int i = -1, len;
char *p;
select_active(NULL, 2);
p = index(str, ' ');
if (p)
{
*p = '\0';
while (*p++ == ' ');
}
else
p = "";
len = strlen(str);
while (cmdlist[++i].name)
{
if (!strncasecmp(cmdlist[i].name, str, len))
break;
}
if (aliases)
{
struct aliaslist_ *atmp = alias;
struct aliaslist_ *acmd = NULL;
while (atmp)
{
if (!strncasecmp(atmp->name, str, len))
{
if (len == strlen(atmp->name))
break;
if (!acmd)
acmd = atmp;
}
atmp = atmp->nexta;
}
if (atmp)
{
if (len != strlen(atmp->name) && acmd)
{
vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
return;
}
acmd = atmp;
if (cmdlist[i].name && len != strlen(acmd->name))
{
vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
return;
}
parse_alias(acmd, p);
return;
}
}
if (cmdlist[i].name)
{
if (len != strlen(cmdlist[i].name)
&& cmdlist[i+1].name && !strncasecmp(cmdlist[i+1].name, str, len))
{
vsic_slog(LOG_CLIENT, "--- %s: Ambiguous command.", str);
return;
}
if (cmdlist[i].func)
switch (cmdlist[i].func(p))
{
case 0:
break;
case -1:
vsic_slog(LOG_CLIENT, "%s: Not enough parameters.",
cmdlist[i].name);
break;
case -2:
vsic_slog(LOG_CLIENT, "%s: No recipient specified.",
cmdlist[i].name);
break;
case -3:
vsic_slog(LOG_CLIENT, "%s: No text to send.", cmdlist[i].name);
break;
case -4:
vsic_slog(LOG_CLIENT, "%s: Invalid parameter(s).",
cmdlist[i].name);
break;
case -5:
vsic_slog(LOG_CLIENT, "%s: Bind the window to a server first!",
cmdlist[i].name);
break;
case -9:
vsic_slog(LOG_CLIENT, "%s: functionality not implemented.",
cmdlist[i].name);
break;
default:
abort(); /* never */
}
else
vsic_write("%s %s", cmdlist[i].name, p);
}
else
vsic_slog(LOG_CLIENT, "%s: Unknown command.", str);
}
syntax highlighted by Code2HTML, v. 0.9.1