mirror of
https://github.com/irssi/irssi.git
synced 2024-11-03 04:27:19 -05:00
452 lines
11 KiB
C
452 lines
11 KiB
C
|
/*
|
||
|
modes.c : irssi
|
||
|
|
||
|
Copyright (C) 1999-2000 Timo Sirainen
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
#include "module.h"
|
||
|
#include "commands.h"
|
||
|
#include "signals.h"
|
||
|
|
||
|
#include "irc.h"
|
||
|
#include "mode-lists.h"
|
||
|
#include "nicklist.h"
|
||
|
|
||
|
/* Change nick's mode in channel */
|
||
|
static void nick_mode_change(CHANNEL_REC *channel, const char *nick, const char mode, gboolean set)
|
||
|
{
|
||
|
NICK_REC *nickrec;
|
||
|
|
||
|
g_return_if_fail(channel != NULL);
|
||
|
g_return_if_fail(nick != NULL);
|
||
|
|
||
|
nickrec = nicklist_find(channel, nick);
|
||
|
if (nickrec == NULL) return; /* No /names list got yet */
|
||
|
|
||
|
if (mode == '@') nickrec->op = set;
|
||
|
if (mode == '+') nickrec->voice = set;
|
||
|
|
||
|
signal_emit("nick mode changed", 2, channel, nickrec);
|
||
|
}
|
||
|
|
||
|
/* Parse channel mode string */
|
||
|
void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mode)
|
||
|
{
|
||
|
char *dup, *modestr, *ptr, *curmode, type;
|
||
|
|
||
|
g_return_if_fail(channel != NULL);
|
||
|
g_return_if_fail(setby != NULL);
|
||
|
g_return_if_fail(modestr != NULL);
|
||
|
|
||
|
type = '+';
|
||
|
|
||
|
dup = modestr = g_strdup(mode);
|
||
|
curmode = cmd_get_param(&modestr);
|
||
|
while (*curmode != '\0') {
|
||
|
switch (*curmode) {
|
||
|
case '+':
|
||
|
case '-':
|
||
|
type = *curmode;
|
||
|
break;
|
||
|
|
||
|
case 'b':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr == '\0') break;
|
||
|
|
||
|
if (type == '+')
|
||
|
banlist_add(channel, ptr, setby, time(NULL));
|
||
|
else
|
||
|
banlist_remove(channel, ptr);
|
||
|
break;
|
||
|
|
||
|
case 'e':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr == '\0') break;
|
||
|
|
||
|
if (type == '+')
|
||
|
banlist_exception_add(channel, ptr, setby, time(NULL));
|
||
|
else
|
||
|
banlist_exception_remove(channel, ptr);
|
||
|
break;
|
||
|
|
||
|
case 'I':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr == '\0') break;
|
||
|
|
||
|
if (type == '+')
|
||
|
invitelist_add(channel, ptr);
|
||
|
else
|
||
|
invitelist_remove(channel, ptr);
|
||
|
break;
|
||
|
|
||
|
case 'v':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr != '\0')
|
||
|
nick_mode_change(channel, ptr, '+', type == '+');
|
||
|
break;
|
||
|
|
||
|
case 'o':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr == '\0') break;
|
||
|
|
||
|
if (strcmp(channel->server->nick, ptr) == 0)
|
||
|
channel->chanop = type == '+' ? TRUE : FALSE;
|
||
|
nick_mode_change(channel, ptr, '@', type == '+');
|
||
|
break;
|
||
|
|
||
|
case 'l':
|
||
|
if (type == '-')
|
||
|
channel->limit = 0;
|
||
|
else {
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
sscanf(ptr, "%d", &channel->limit);
|
||
|
}
|
||
|
signal_emit("channel mode changed", 1, channel);
|
||
|
break;
|
||
|
case 'k':
|
||
|
ptr = cmd_get_param(&modestr);
|
||
|
if (*ptr != '\0' || type == '-') {
|
||
|
g_free_and_null(channel->key);
|
||
|
if (type == '+') {
|
||
|
channel->key = g_strdup(ptr);
|
||
|
channel->mode_key = TRUE;
|
||
|
}
|
||
|
}
|
||
|
signal_emit("channel mode changed", 1, channel);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
switch (*curmode) {
|
||
|
case 'i':
|
||
|
channel->mode_invite = type == '+';
|
||
|
break;
|
||
|
case 'm':
|
||
|
channel->mode_moderate = type == '+';
|
||
|
break;
|
||
|
case 's':
|
||
|
channel->mode_secret = type == '+';
|
||
|
break;
|
||
|
case 'p':
|
||
|
channel->mode_private = type == '+';
|
||
|
break;
|
||
|
case 'n':
|
||
|
channel->mode_nomsgs = type == '+';
|
||
|
break;
|
||
|
case 't':
|
||
|
channel->mode_optopic = type == '+';
|
||
|
break;
|
||
|
case 'a':
|
||
|
channel->mode_anonymous = type == '+';
|
||
|
break;
|
||
|
case 'r':
|
||
|
channel->mode_reop = type == '+';
|
||
|
break;
|
||
|
}
|
||
|
signal_emit("channel mode changed", 1, channel);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
curmode++;
|
||
|
}
|
||
|
g_free(dup);
|
||
|
|
||
|
if (!channel->mode_key && channel->key != NULL) {
|
||
|
/* join was used with key but there's no key set
|
||
|
in channel modes.. */
|
||
|
g_free(channel->key);
|
||
|
channel->key = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int compare_char(const void *p1, const void *p2)
|
||
|
{
|
||
|
const char *c1 = p1, *c2 = p2;
|
||
|
|
||
|
return *c1 < *c2 ? -1 :
|
||
|
(*c1 > *c2 ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
/* add `mode' to `old' - return newly allocated mode. */
|
||
|
char *modes_join(const char *old, const char *mode)
|
||
|
{
|
||
|
GString *newmode;
|
||
|
char type, *p;
|
||
|
|
||
|
g_return_val_if_fail(mode != NULL, NULL);
|
||
|
|
||
|
type = '+';
|
||
|
newmode = g_string_new(old);
|
||
|
while (*mode != '\0' && *mode != ' ') {
|
||
|
if (*mode == '+' || *mode == '-') {
|
||
|
type = *mode;
|
||
|
} else {
|
||
|
p = strchr(newmode->str, *mode);
|
||
|
|
||
|
if (type == '+' && p == NULL)
|
||
|
g_string_append_c(newmode, *mode);
|
||
|
else if (type == '-' && p != NULL)
|
||
|
g_string_erase(newmode, (int) (p-newmode->str), 1);
|
||
|
}
|
||
|
|
||
|
mode++;
|
||
|
}
|
||
|
|
||
|
qsort(newmode->str, sizeof(char), newmode->len, compare_char);
|
||
|
|
||
|
p = newmode->str;
|
||
|
g_string_free(newmode, FALSE);
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
/* Parse user mode string */
|
||
|
static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
|
||
|
{
|
||
|
char *newmode, *oldmode;
|
||
|
|
||
|
g_return_if_fail(server != NULL);
|
||
|
g_return_if_fail(modestr != NULL);
|
||
|
|
||
|
newmode = modes_join(server->usermode, modestr);
|
||
|
oldmode = server->usermode;
|
||
|
server->usermode = newmode;
|
||
|
server->server_operator = (strchr(newmode, 'o') != NULL);
|
||
|
|
||
|
signal_emit("user mode changed", 2, server, oldmode);
|
||
|
g_free_not_null(oldmode);
|
||
|
}
|
||
|
|
||
|
static void event_user_mode(const char *data, IRC_SERVER_REC *server)
|
||
|
{
|
||
|
char *params, *nick, *mode;
|
||
|
|
||
|
g_return_if_fail(data != NULL);
|
||
|
|
||
|
params = event_get_params(data, 3, NULL, &nick, &mode);
|
||
|
parse_user_mode(server, mode);
|
||
|
|
||
|
g_free(params);
|
||
|
}
|
||
|
|
||
|
static void event_mode(const char *data, IRC_SERVER_REC *server, const char *nick)
|
||
|
{
|
||
|
CHANNEL_REC *chanrec;
|
||
|
char *params, *channel, *mode;
|
||
|
|
||
|
g_return_if_fail(data != NULL);
|
||
|
|
||
|
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &channel, &mode);
|
||
|
|
||
|
if (!ischannel(*channel)) {
|
||
|
/* user mode change */
|
||
|
parse_user_mode(server, mode);
|
||
|
} else {
|
||
|
/* channel mode change */
|
||
|
chanrec = channel_find(server, channel);
|
||
|
if (chanrec != NULL)
|
||
|
parse_channel_modes(chanrec, nick, mode);
|
||
|
}
|
||
|
|
||
|
g_free(params);
|
||
|
}
|
||
|
|
||
|
static void event_away(const char *data, IRC_SERVER_REC *server)
|
||
|
{
|
||
|
g_return_if_fail(server != NULL);
|
||
|
|
||
|
server->usermode_away = TRUE;
|
||
|
signal_emit("away mode changed", 1, server);
|
||
|
}
|
||
|
|
||
|
static void event_unaway(const char *data, IRC_SERVER_REC *server)
|
||
|
{
|
||
|
g_return_if_fail(server != NULL);
|
||
|
|
||
|
server->usermode_away = FALSE;
|
||
|
g_free_and_null(server->away_reason);
|
||
|
signal_emit("away mode changed", 1, server);
|
||
|
}
|
||
|
|
||
|
void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const char *nicks, const char *mode)
|
||
|
{
|
||
|
GString *str;
|
||
|
int num, modepos;
|
||
|
char **nick, **nicklist;
|
||
|
|
||
|
g_return_if_fail(server != NULL);
|
||
|
g_return_if_fail(channel != NULL);
|
||
|
g_return_if_fail(nicks != NULL);
|
||
|
g_return_if_fail(mode != NULL);
|
||
|
if (*nicks == '\0') return;
|
||
|
|
||
|
num = modepos = 0;
|
||
|
str = g_string_new(NULL);
|
||
|
|
||
|
nicklist = g_strsplit(nicks, " ", -1);
|
||
|
for (nick = nicklist; *nick != NULL; nick++) {
|
||
|
if (num == 0)
|
||
|
{
|
||
|
g_string_sprintf(str, "MODE %s %s", channel, mode);
|
||
|
modepos = str->len;
|
||
|
} else {
|
||
|
/* insert the mode string */
|
||
|
g_string_insert(str, modepos, mode);
|
||
|
}
|
||
|
|
||
|
g_string_sprintfa(str, " %s", *nick);
|
||
|
|
||
|
if (++num == server->connrec->max_modes) {
|
||
|
/* max. modes / command reached, send to server */
|
||
|
irc_send_cmd(server, str->str);
|
||
|
num = 0;
|
||
|
}
|
||
|
}
|
||
|
if (num > 0) irc_send_cmd(server, str->str);
|
||
|
|
||
|
g_strfreev(nicklist);
|
||
|
g_string_free(str, TRUE);
|
||
|
}
|
||
|
|
||
|
#define MODE_HAS_ARG(c) ((c) == 'b' || (c) == 'e' || (c) == 'I' || \
|
||
|
(c) == 'v' || (c) == 'o' || (c) == 'l' || (c) == 'k')
|
||
|
|
||
|
void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *mode)
|
||
|
{
|
||
|
char *modestr, *curmode, type, *orig;
|
||
|
GString *tmode, *targs;
|
||
|
int count;
|
||
|
|
||
|
g_return_if_fail(server != NULL);
|
||
|
g_return_if_fail(channel != NULL);
|
||
|
g_return_if_fail(modestr != NULL);
|
||
|
|
||
|
tmode = g_string_new(NULL);
|
||
|
targs = g_string_new(NULL);
|
||
|
type = '+'; count = 0;
|
||
|
|
||
|
orig = modestr = g_strdup(mode);
|
||
|
|
||
|
curmode = cmd_get_param(&modestr);
|
||
|
for (; *curmode != '\0'; curmode++) {
|
||
|
if (*curmode == '+' || *curmode == '-') {
|
||
|
type = *curmode;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (count == server->connrec->max_modes && MODE_HAS_ARG(*curmode)) {
|
||
|
irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
|
||
|
|
||
|
count = 0;
|
||
|
g_string_truncate(tmode, 0);
|
||
|
g_string_truncate(targs, 0);
|
||
|
}
|
||
|
|
||
|
g_string_append_c(tmode, *curmode);
|
||
|
|
||
|
if (MODE_HAS_ARG(*curmode)) {
|
||
|
char *arg;
|
||
|
|
||
|
count++;
|
||
|
arg = cmd_get_param(&modestr);
|
||
|
if (*arg != '\0') g_string_sprintfa(targs, " %s", arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tmode->len > 0)
|
||
|
irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
|
||
|
|
||
|
g_string_free(tmode, TRUE);
|
||
|
g_string_free(targs, TRUE);
|
||
|
g_free(orig);
|
||
|
}
|
||
|
|
||
|
static void cmd_op(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
|
||
|
{
|
||
|
if (!irc_item_channel(item)) return;
|
||
|
channel_set_singlemode(server, item->name, data, "+o");
|
||
|
}
|
||
|
|
||
|
static void cmd_deop(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
|
||
|
{
|
||
|
if (!irc_item_channel(item)) return;
|
||
|
channel_set_singlemode(server, item->name, data, "-o");
|
||
|
}
|
||
|
|
||
|
static void cmd_voice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
|
||
|
{
|
||
|
if (!irc_item_channel(item)) return;
|
||
|
channel_set_singlemode(server, item->name, data, "+v");
|
||
|
}
|
||
|
|
||
|
static void cmd_devoice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
|
||
|
{
|
||
|
if (!irc_item_channel(item)) return;
|
||
|
channel_set_singlemode(server, item->name, data, "-v");
|
||
|
}
|
||
|
|
||
|
static void cmd_mode(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
|
||
|
{
|
||
|
char *params, *target, *mode;
|
||
|
|
||
|
g_return_if_fail(data != NULL);
|
||
|
if (server == NULL || !server->connected || !irc_server_check(server))
|
||
|
cmd_return_error(CMDERR_NOT_CONNECTED);
|
||
|
|
||
|
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &mode);
|
||
|
if (strcmp(target, "*") == 0) {
|
||
|
if (!irc_item_channel(item))
|
||
|
cmd_return_error(CMDERR_NOT_JOINED);
|
||
|
|
||
|
target = item->name;
|
||
|
}
|
||
|
if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
|
||
|
|
||
|
if (ischannel(*target))
|
||
|
channel_set_mode(server, target, mode);
|
||
|
else
|
||
|
irc_send_cmdv(server, "MODE %s %s", target, mode);
|
||
|
|
||
|
g_free(params);
|
||
|
}
|
||
|
|
||
|
void modes_init(void)
|
||
|
{
|
||
|
signal_add("event 221", (SIGNAL_FUNC) event_user_mode);
|
||
|
signal_add("event 305", (SIGNAL_FUNC) event_unaway);
|
||
|
signal_add("event 306", (SIGNAL_FUNC) event_away);
|
||
|
signal_add("event mode", (SIGNAL_FUNC) event_mode);
|
||
|
|
||
|
command_bind("op", NULL, (SIGNAL_FUNC) cmd_op);
|
||
|
command_bind("deop", NULL, (SIGNAL_FUNC) cmd_deop);
|
||
|
command_bind("voice", NULL, (SIGNAL_FUNC) cmd_voice);
|
||
|
command_bind("devoice", NULL, (SIGNAL_FUNC) cmd_devoice);
|
||
|
command_bind("mode", NULL, (SIGNAL_FUNC) cmd_mode);
|
||
|
}
|
||
|
|
||
|
void modes_deinit(void)
|
||
|
{
|
||
|
signal_remove("event 221", (SIGNAL_FUNC) event_user_mode);
|
||
|
signal_remove("event 305", (SIGNAL_FUNC) event_unaway);
|
||
|
signal_remove("event 306", (SIGNAL_FUNC) event_away);
|
||
|
signal_remove("event mode", (SIGNAL_FUNC) event_mode);
|
||
|
|
||
|
command_unbind("op", (SIGNAL_FUNC) cmd_op);
|
||
|
command_unbind("deop", (SIGNAL_FUNC) cmd_deop);
|
||
|
command_unbind("voice", (SIGNAL_FUNC) cmd_voice);
|
||
|
command_unbind("devoice", (SIGNAL_FUNC) cmd_devoice);
|
||
|
command_unbind("mode", (SIGNAL_FUNC) cmd_mode);
|
||
|
}
|