/* * 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); }