hirc

IRC client
Log | Files | Refs

commit a6a17b5749f1524a348d37e4546e19a57d9302c5
parent 628c770832d884749a00a4a38f2b9c1f8d50b0b2
Author: hhvn <dev@hhvn.uk>
Date:   Tue,  8 Mar 2022 19:29:58 +0000

Commands for managing priviledges (eg, op/voice/deop/devoice)

Diffstat:
Msrc/commands.c | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/config.c | 7+++++++
Msrc/struct.h | 7+++++--
3 files changed, 211 insertions(+), 53 deletions(-)

diff --git a/src/commands.c b/src/commands.c @@ -33,7 +33,7 @@ #define command_toomany(cmd) ui_error("/%s: too many arguments", cmd) #define command_needselected(cmd, type) ui_error("/%s: no %s selected", cmd, type) -#define COMMAND(func) static void func(struct Server *server, char *str) +#define COMMAND(func) static void func(struct Server *server, struct Channel *channel, char *str) COMMAND(command_away); COMMAND(command_msg); @@ -72,6 +72,17 @@ COMMAND(command_source); COMMAND(command_dump); COMMAND(command_close); +COMMAND(command_op); +COMMAND(command_voice); +COMMAND(command_halfop); +COMMAND(command_admin); +COMMAND(command_owner); +COMMAND(command_deop); +COMMAND(command_devoice); +COMMAND(command_dehalfop); +COMMAND(command_deadmin); +COMMAND(command_deowner); + static char *command_optarg; enum { opt_error = -2, @@ -234,6 +245,36 @@ struct Command commands[] = { {"close", command_close, 0, { "usage: /close [id]", "Forget about selected buffer, or a buffer by id.", NULL}}, + {"op", command_op, 2, { + "usage: /op nicks...", + "Give a nickname +o on the current channel.", NULL}}, + {"voice", command_voice, 2, { + "usage: /voice nicks...", + "Give a nickname +v on the current channel.", NULL}}, + {"halfop", command_halfop, 2, { + "usage: /halfop nicks...", + "Give a nickname +h on the current channel.", NULL}}, + {"admin", command_admin, 2, { + "usage: /admin nicks...", + "Give a nickname +a on the current channel.", NULL}}, + {"owner", command_owner, 2, { + "usage: /owner nicks...", + "Give a nickname +q on the current channel.", NULL}}, + {"deop", command_deop, 2, { + "usage: /deop nicks...", + "Remove +o for a nick on the current channel.", NULL}}, + {"devoice", command_devoice, 2, { + "usage: /devoice nicks...", + "Remove +v for a nick on the current channel.", NULL}}, + {"dehalfop", command_dehalfop, 2, { + "usage: /dehalfop nicks...", + "Remove +h for a nick on the current channel.", NULL}}, + {"deadmin", command_deadmin, 2, { + "usage: /deadmin nicks...", + "Remove +a for a nick on the current channel.", NULL}}, + {"deowner", command_deowner, 2, { + "usage: /deowner nicks...", + "Remove +q for a nick on the current channel.", NULL}}, {NULL, NULL}, }; @@ -431,34 +472,34 @@ command_join) { COMMAND( command_part) { - char *channel = NULL, *reason = NULL; + char *chan = NULL, *reason = NULL; char msg[512]; if (str) { if (serv_ischannel(server, str)) - channel = strtok_r(str, " ", &reason); + chan = strtok_r(str, " ", &reason); else reason = str; } - if (!channel) { + if (!chan) { if (selected.channel) { - channel = selected.channel->name; + chan = selected.channel->name; } else { command_toofew("part"); return; } } - snprintf(msg, sizeof(msg), "PART %s :%s\r\n", channel, reason ? reason : config_gets("misc.partmessage")); + snprintf(msg, sizeof(msg), "PART %s :%s\r\n", chan, reason ? reason : config_gets("misc.partmessage")); ircprintf(server, "%s", msg); - expect_set(server, Expect_part, channel); + expect_set(server, Expect_part, chan); } COMMAND( command_kick) { - char *channel, *nick, *reason; + char *chan, *nick, *reason; char *s; if (!str) { @@ -469,7 +510,7 @@ command_kick) { s = strtok_r(str, " ", &reason); if (serv_ischannel(server, s)) { - channel = s; + chan = s; nick = strtok_r(NULL, " ", &reason); } else { if (selected.channel == NULL) { @@ -477,33 +518,33 @@ command_kick) { return; } - channel = selected.channel->name; + chan = selected.channel->name; nick = s; } if (reason) - ircprintf(server, "KICK %s %s :%s\r\n", channel, nick, reason); + ircprintf(server, "KICK %s %s :%s\r\n", chan, nick, reason); else - ircprintf(server, "KICK %s %s\r\n", channel, nick); + ircprintf(server, "KICK %s %s\r\n", chan, nick); } COMMAND( command_mode) { - char *channel, *modes; + char *chan, *modes; char *s = NULL; if (str) s = strtok_r(str, " ", &modes); if (serv_ischannel(server, s)) { - channel = s; + chan = s; } else { if (selected.channel == NULL) { command_needselected("mode", "channel"); return; } - channel = selected.channel->name; + chan = selected.channel->name; if (modes) { *(modes - 1) = ' '; modes = s; @@ -511,10 +552,10 @@ command_mode) { } if (modes) { - ircprintf(server, "MODE %s %s\r\n", channel, modes); + ircprintf(server, "MODE %s %s\r\n", chan, modes); } else { - expect_set(server, Expect_channelmodeis, channel); - ircprintf(server, "MODE %s\r\n", channel); + expect_set(server, Expect_channelmodeis, chan); + ircprintf(server, "MODE %s\r\n", chan); } } @@ -851,7 +892,7 @@ command_format) { len = strlen(str) + strlen("format.") + 1; newstr = talloc(len); snprintf(newstr, len, "format.%s", str); - command_set(server, newstr); + command_set(server, channel, newstr); } COMMAND( @@ -908,7 +949,7 @@ command_server) { for (i=0; commands[i].name && commands[i].func; i++) { if (strcmp(commands[i].name, cmd) == 0) { - commands[i].func(nserver, arg); + commands[i].func(nserver, channel, arg); return; } } @@ -947,13 +988,13 @@ command_server) { COMMAND( command_names) { - char *channel, *save = NULL; + char *chan, *save = NULL; - channel = strtok_r(str, " ", &save); - if (!channel) - channel = selected.channel ? selected.channel->name : NULL; + chan = strtok_r(str, " ", &save); + if (!chan) + chan = selected.channel ? selected.channel->name : NULL; - if (!channel) { + if (!chan) { command_needselected("names", "channel"); return; } @@ -963,13 +1004,13 @@ command_names) { return; } - ircprintf(server, "NAMES %s\r\n", channel); - expect_set(server, Expect_names, channel); + ircprintf(server, "NAMES %s\r\n", chan); + expect_set(server, Expect_names, chan); } COMMAND( command_topic) { - char *channel, *topic = NULL; + char *chan, *topic = NULL; int clear = 0, ret; enum { opt_clear, }; struct CommandOpts opts[] = { @@ -988,18 +1029,18 @@ command_topic) { } if (str) - channel = strtok_r(str, " ", &topic); + chan = strtok_r(str, " ", &topic); else - channel = topic = NULL; + chan = topic = NULL; - if (channel && !serv_ischannel(server, channel)) { - topic = channel; - channel = NULL; + if (chan && !serv_ischannel(server, chan)) { + topic = chan; + chan = NULL; } - if (!channel && selected.channel) { - channel = selected.channel->name; - } else if (!channel) { + if (!chan && selected.channel) { + chan = selected.channel->name; + } else if (!chan) { command_needselected("topic", "channel"); return; } @@ -1009,14 +1050,14 @@ command_topic) { command_toomany("topic"); return; } - ircprintf(server, "TOPIC %s :\r\n", channel); + ircprintf(server, "TOPIC %s :\r\n", chan); return; } if (!topic) { - ircprintf(server, "TOPIC %s\r\n", channel); - expect_set(server, Expect_topic, channel); - } else ircprintf(server, "TOPIC %s :%s\r\n", channel, topic); + ircprintf(server, "TOPIC %s\r\n", chan); + expect_set(server, Expect_topic, chan); + } else ircprintf(server, "TOPIC %s :%s\r\n", chan, topic); } COMMAND( @@ -1070,9 +1111,8 @@ command_bind) { } } -static -void -command_alias(struct Server *server, char *str) { +COMMAND( +command_alias) { struct Alias *p; char *alias = NULL, *cmd = NULL; int delete = 0, ret; @@ -1129,7 +1169,7 @@ command_help) { int i, j; if (!str) { - command_help(server, "/help"); + command_help(server, channel, "/help"); return; } @@ -1187,9 +1227,8 @@ command_echo) { hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP, "SELF_UI :%s", str); } -static -void -command_grep(struct Server *server, char *str) { +COMMAND( +command_grep) { struct History *p; regex_t re; int regopt = 0, ret; @@ -1516,6 +1555,111 @@ command_close) { } } +static void +modelset(struct Server *server, struct Channel *channel, + int remove, char mode, char *args) { + char *percmds, *p; + char *modes; + int percmd; + int i; + size_t len; + + percmds = support_get(server, "MODES"); + if (percmds) + percmd = atoi(percmds); + else + percmd = config_getl("def.modes"); + + /* + * Now, I'd hope that servers do something clever like determining this + * value from the max length of channels, nicks and IRC messages + * overall, so that it is impossible to send messages that are too + * long, but I doubt it. + * TODO: some maths for limiting percmd based on lengths. + * + * It'd be useful to be able to check if the desired priviledge is + * supported by the server. Theoretically some servers could also use + * different modes for priviledges, in this case it would make sense + * that 'char mode' be the symbol, i,e, ~&@%+ and then we would look up + * the mode in PREFIX. + * TODO: check PREFIX=... + * + */ + + /* 2 = null byte and +/- */ + len = percmd + 2; + modes = talloc(len); + *modes = remove ? '-' : '+'; + for (i = 0; i < percmd; i++) + *(modes + i + 1) = mode; + *(modes + len - 1) = '\0'; + + do { + for (i = 0, p = args; *p && i < percmd; p++) + if (*p == ' ' && *(p+1) != ' ') + i++; + if (*p) + *(p - 1) = '\0'; + else + i++; + *(modes + i + 1) = '\0'; + + ircprintf(server, "MODE %s %s %s\r\n", channel->name, modes, args); + + args = p; + } while (*args); +} + +COMMAND( +command_op) { + modelset(server, channel, 0, 'o', str); +} + +COMMAND( +command_voice) { + modelset(server, channel, 0, 'v', str); +} + +COMMAND( +command_halfop) { + modelset(server, channel, 0, 'h', str); +} + +COMMAND( +command_admin) { + modelset(server, channel, 0, 'a', str); +} + +COMMAND( +command_owner) { + modelset(server, channel, 0, 'q', str); +} + +COMMAND( +command_deop) { + modelset(server, channel, 1, 'o', str); +} + +COMMAND( +command_devoice) { + modelset(server, channel, 1, 'v', str); +} + +COMMAND( +command_dehalfop) { + modelset(server, channel, 1, 'h', str); +} + +COMMAND( +command_deadmin) { + modelset(server, channel, 1, 'a', str); +} + +COMMAND( +command_deowner) { + modelset(server, channel, 1, 'q', str); +} + int command_getopt(char **str, struct CommandOpts *opts) { char *opt; @@ -1674,11 +1818,15 @@ command_eval(struct Server *server, char *str) { for (cmdp = commands; cmdp->name && cmdp->func; cmdp++) { if (strcmp(cmdp->name, cmd) == 0) { - if (cmdp->needserver && !server) { + if (cmdp->need == 2 && !selected.channel) + ui_error("/%s requires a channel to be selected", cmdp->name); + else if (cmdp->need == 2 && selected.channel->server != server) + ui_error("/%s cannot be run with /server", cmdp->name); + else if (cmdp->need == 1 && !server) ui_error("/%s requires a server to be selected or provided by /server", cmdp->name); - } else { - cmdp->func(server, s); - } + else + cmdp->func(server, selected.channel, s); + return; } } diff --git a/src/config.c b/src/config.c @@ -88,6 +88,13 @@ struct Config config[] = { "If a server doesn't supply this in the nonstandard", "RPL_ISUPPORT, it likely won't support nonstandard", "prefixes.", NULL}}, + {"def.modes", 1, Val_signed, + .num = 1, + .numhandle = NULL, + .description = { + "You most likely don't want to touch this.", + "If a server doesn't send MODES=... in RPL_ISUPPORT,", + "use this number instead.", NULL}}, {"reconnect.interval", 1, Val_nzunsigned, .num = 10, .numhandle = NULL, diff --git a/src/struct.h b/src/struct.h @@ -170,8 +170,11 @@ struct Handler { /* commands received from user */ struct Command { char *name; - void (*func)(struct Server *server, char *str); - int needserver; + void (*func)(struct Server *server, struct Channel *channel, char *str); + int need; /* 0 - nothing + 1 - server + 2 - channel (and implicitly server) + 3+ - implementation defined (pfft. who's gonna reimplement hirc?) */ char *description[64]; };