2000-04-26 04:03:38 -04:00
|
|
|
/*
|
|
|
|
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"
|
2000-05-25 07:30:47 -04:00
|
|
|
#include "modes.h"
|
2000-04-26 04:03:38 -04:00
|
|
|
#include "mode-lists.h"
|
|
|
|
#include "nicklist.h"
|
|
|
|
|
|
|
|
/* Change nick's mode in channel */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *nick,
|
2000-08-20 03:17:13 -04:00
|
|
|
const char mode, int type)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
|
|
|
NICK_REC *nickrec;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_if_fail(IS_IRC_CHANNEL(channel));
|
2000-04-26 04:03:38 -04:00
|
|
|
g_return_if_fail(nick != NULL);
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
nickrec = nicklist_find(CHANNEL(channel), nick);
|
2000-04-26 04:03:38 -04:00
|
|
|
if (nickrec == NULL) return; /* No /names list got yet */
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
if (mode == '@') nickrec->op = type == '+';
|
|
|
|
if (mode == '+') nickrec->voice = type == '+';
|
|
|
|
if (mode == '%') nickrec->halfop = type == '+';
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
signal_emit("nick mode changed", 2, channel, nickrec);
|
|
|
|
}
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
static int mode_is_set(const char *str, char mode)
|
|
|
|
{
|
|
|
|
char *end, *pos;
|
|
|
|
|
|
|
|
g_return_val_if_fail(str != NULL, FALSE);
|
|
|
|
|
|
|
|
end = strchr(str, ' ');
|
|
|
|
pos = strchr(str, mode);
|
|
|
|
return pos != NULL && (end == NULL || pos < end);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add argument to specified position */
|
2000-08-25 20:35:32 -04:00
|
|
|
static void mode_add_arg(GString *str, int pos, int updating, const char *arg)
|
2000-08-20 03:17:13 -04:00
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
for (p = str->str; *p != '\0'; p++) {
|
|
|
|
if (*p != ' ')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pos == 0)
|
|
|
|
break;
|
|
|
|
pos--;
|
|
|
|
}
|
|
|
|
|
2000-08-25 20:35:32 -04:00
|
|
|
pos = (int) (p-str->str);
|
|
|
|
if (updating && *p != '\0') {
|
|
|
|
/* remove the old argument */
|
|
|
|
p++;
|
|
|
|
while (*p != '\0' && *p != ' ') p++;
|
|
|
|
g_string_erase(str, pos, (int) (p-str->str)-pos);
|
|
|
|
}
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
/* .. GLib shouldn't fail when inserting at the end of the string */
|
2000-08-25 20:35:32 -04:00
|
|
|
if (pos == str->len) {
|
2000-08-20 03:17:13 -04:00
|
|
|
g_string_append_c(str, ' ');
|
|
|
|
g_string_append(str, arg);
|
|
|
|
} else {
|
|
|
|
g_string_insert_c(str, pos, ' ');
|
|
|
|
g_string_insert(str, pos+1, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add mode character to list sorted alphabetically */
|
|
|
|
static void mode_add_sorted(GString *str, char mode, const char *arg)
|
|
|
|
{
|
|
|
|
char *p;
|
2000-08-25 20:35:32 -04:00
|
|
|
int updating, argpos = 0;
|
2000-08-20 03:17:13 -04:00
|
|
|
|
|
|
|
/* check that mode isn't already set */
|
2000-08-25 20:35:32 -04:00
|
|
|
if (!HAS_MODE_ARG_SET(mode) && mode_is_set(str->str, mode))
|
2000-08-20 03:17:13 -04:00
|
|
|
return;
|
|
|
|
|
2000-08-25 20:35:32 -04:00
|
|
|
updating = FALSE;
|
2000-08-20 03:17:13 -04:00
|
|
|
for (p = str->str; *p != '\0' && *p != ' '; p++) {
|
|
|
|
if (mode < *p)
|
|
|
|
break;
|
2000-08-25 20:35:32 -04:00
|
|
|
if (mode == *p) {
|
|
|
|
updating = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2000-08-20 03:17:13 -04:00
|
|
|
if (HAS_MODE_ARG_SET(*p))
|
|
|
|
argpos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .. GLib shouldn't fail when inserting at the end of the string */
|
2000-08-25 20:35:32 -04:00
|
|
|
if (!updating) {
|
|
|
|
if (*p == '\0')
|
|
|
|
g_string_append_c(str, mode);
|
|
|
|
else
|
|
|
|
g_string_insert_c(str, (int) (p-str->str), mode);
|
|
|
|
}
|
2000-08-20 03:17:13 -04:00
|
|
|
if (arg != NULL)
|
2000-08-25 20:35:32 -04:00
|
|
|
mode_add_arg(str, argpos, updating, arg);
|
2000-08-20 03:17:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* remove the n'th argument */
|
|
|
|
static void node_remove_arg(GString *str, int pos)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int startpos;
|
|
|
|
|
|
|
|
startpos = -1;
|
|
|
|
for (p = str->str; *p != '\0'; p++) {
|
|
|
|
if (*p != ' ')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
break;
|
|
|
|
if (pos == 0)
|
|
|
|
startpos = (int) (p-str->str);
|
|
|
|
pos--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startpos == -1)
|
|
|
|
return; /* not found */
|
|
|
|
|
|
|
|
g_string_erase(str, startpos, (int) (p-str->str)-startpos);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove mode (and it's argument) from string */
|
|
|
|
static void mode_remove(GString *str, char mode)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int argpos = 0;
|
|
|
|
|
|
|
|
for (p = str->str; *p != '\0' && *p != ' '; p++) {
|
|
|
|
if (mode == *p) {
|
|
|
|
g_string_erase(str, (int) (p-str->str), 1);
|
|
|
|
if (HAS_MODE_ARG_SET(mode))
|
|
|
|
node_remove_arg(str, argpos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (HAS_MODE_ARG_SET(*p))
|
|
|
|
argpos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mode_set(GString *str, char type, char mode)
|
|
|
|
{
|
|
|
|
g_return_if_fail(str != NULL);
|
|
|
|
|
|
|
|
if (type == '-')
|
|
|
|
mode_remove(str, mode);
|
|
|
|
else
|
|
|
|
mode_add_sorted(str, mode, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mode_set_arg(GString *str, char type, char mode, const char *arg)
|
|
|
|
{
|
|
|
|
g_return_if_fail(str != NULL);
|
|
|
|
g_return_if_fail(type == '-' || arg != NULL);
|
|
|
|
|
|
|
|
if (type == '-')
|
|
|
|
mode_remove(str, mode);
|
|
|
|
else
|
|
|
|
mode_add_sorted(str, mode, arg);
|
|
|
|
}
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode)
|
2000-08-20 03:17:13 -04:00
|
|
|
{
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_val_if_fail(IS_IRC_CHANNEL(channel), FALSE);
|
2000-08-20 03:17:13 -04:00
|
|
|
|
|
|
|
return channel->mode == NULL ? FALSE :
|
|
|
|
mode_is_set(channel->mode, mode);
|
|
|
|
}
|
|
|
|
|
2000-04-26 04:03:38 -04:00
|
|
|
/* Parse channel mode string */
|
2000-08-26 11:39:44 -04:00
|
|
|
void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
|
2000-08-20 03:17:13 -04:00
|
|
|
const char *mode)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-08-20 03:17:13 -04:00
|
|
|
GString *newmode;
|
|
|
|
char *dup, *modestr, *arg, *curmode, type;
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_if_fail(IS_IRC_CHANNEL(channel));
|
2000-06-06 12:58:06 -04:00
|
|
|
g_return_if_fail(mode != NULL);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
type = '+';
|
2000-08-20 03:17:13 -04:00
|
|
|
newmode = g_string_new(channel->mode);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
dup = modestr = g_strdup(mode);
|
|
|
|
curmode = cmd_get_param(&modestr);
|
|
|
|
while (*curmode != '\0') {
|
2000-08-20 03:17:13 -04:00
|
|
|
if (HAS_MODE_ARG(type, *curmode)) {
|
|
|
|
/* get the argument for the mode. since we're
|
|
|
|
expecting argument, ignore the mode if there's
|
|
|
|
no argument (shouldn't happen). */
|
|
|
|
arg = cmd_get_param(&modestr);
|
2000-10-07 11:12:50 -04:00
|
|
|
if (*arg == '\0') {
|
|
|
|
curmode++;
|
2000-08-20 03:17:13 -04:00
|
|
|
continue;
|
2000-10-07 11:12:50 -04:00
|
|
|
}
|
2000-08-20 03:17:13 -04:00
|
|
|
} else {
|
|
|
|
arg = NULL;
|
|
|
|
}
|
|
|
|
|
2000-04-26 04:03:38 -04:00
|
|
|
switch (*curmode) {
|
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
type = *curmode;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'b':
|
|
|
|
if (type == '+')
|
2000-08-20 03:17:13 -04:00
|
|
|
banlist_add(channel, arg, setby, time(NULL));
|
2000-04-26 04:03:38 -04:00
|
|
|
else
|
2000-08-20 03:17:13 -04:00
|
|
|
banlist_remove(channel, arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
if (type == '+')
|
2000-08-20 03:17:13 -04:00
|
|
|
banlist_exception_add(channel, arg, setby,
|
|
|
|
time(NULL));
|
2000-04-26 04:03:38 -04:00
|
|
|
else
|
2000-08-20 03:17:13 -04:00
|
|
|
banlist_exception_remove(channel, arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
if (type == '+')
|
2000-08-20 03:17:13 -04:00
|
|
|
invitelist_add(channel, arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
else
|
2000-08-20 03:17:13 -04:00
|
|
|
invitelist_remove(channel, arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'o':
|
2000-08-20 03:17:13 -04:00
|
|
|
if (g_strcasecmp(channel->server->nick, arg) == 0)
|
|
|
|
channel->chanop = type == '+';
|
|
|
|
nick_mode_change(channel, arg, '@', type);
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
nick_mode_change(channel, arg, '%', type);
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
nick_mode_change(channel, arg, '+', type);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'l':
|
2000-08-20 03:17:13 -04:00
|
|
|
mode_set_arg(newmode, type, 'l', arg);
|
|
|
|
channel->limit = type == '-' ? 0 : atoi(arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
case 'k':
|
2000-08-20 03:17:13 -04:00
|
|
|
mode_set_arg(newmode, type, 'k', arg);
|
|
|
|
g_free_and_null(channel->key);
|
|
|
|
if (type == '+')
|
|
|
|
channel->key = g_strdup(arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2000-08-20 03:17:13 -04:00
|
|
|
mode_set(newmode, type, *curmode);
|
2000-04-26 04:03:38 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
curmode++;
|
|
|
|
}
|
|
|
|
g_free(dup);
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
if (strchr(channel->mode, 'k') == NULL && channel->key != NULL) {
|
2000-04-26 04:03:38 -04:00
|
|
|
/* join was used with key but there's no key set
|
|
|
|
in channel modes.. */
|
|
|
|
g_free(channel->key);
|
|
|
|
channel->key = NULL;
|
|
|
|
}
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
if (strcmp(newmode->str, channel->mode) != 0) {
|
|
|
|
g_free(channel->mode);
|
|
|
|
channel->mode = g_strdup(newmode->str);
|
|
|
|
|
|
|
|
signal_emit("channel mode changed", 1, channel);
|
|
|
|
}
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
g_string_free(newmode, TRUE);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-10-13 18:05:57 -04:00
|
|
|
/* add `mode' to `old' - return newly allocated mode.
|
|
|
|
`channel' specifies if we're parsing channel mode and we should try
|
|
|
|
to join mode arguments too. */
|
|
|
|
char *modes_join(const char *old, const char *mode, int channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
|
|
|
GString *newmode;
|
2000-08-20 03:17:13 -04:00
|
|
|
char *dup, *modestr, *curmode, type;
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
g_return_val_if_fail(mode != NULL, NULL);
|
|
|
|
|
|
|
|
type = '+';
|
|
|
|
newmode = g_string_new(old);
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
dup = modestr = g_strdup(mode);
|
|
|
|
curmode = cmd_get_param(&modestr);
|
|
|
|
while (*curmode != '\0' && *curmode != ' ') {
|
|
|
|
if (*curmode == '+' || *curmode == '-') {
|
|
|
|
type = *curmode;
|
|
|
|
curmode++;
|
|
|
|
continue;
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-10-13 18:05:57 -04:00
|
|
|
if (!channel || !HAS_MODE_ARG(type, *curmode))
|
2000-08-20 03:17:13 -04:00
|
|
|
mode_set(newmode, type, *curmode);
|
|
|
|
else {
|
|
|
|
mode_set_arg(newmode, type, *curmode,
|
|
|
|
cmd_get_param(&modestr));
|
|
|
|
}
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
curmode++;
|
|
|
|
}
|
|
|
|
g_free(dup);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
modestr = newmode->str;
|
2000-04-26 04:03:38 -04:00
|
|
|
g_string_free(newmode, FALSE);
|
2000-08-20 03:17:13 -04:00
|
|
|
return modestr;
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse user mode string */
|
|
|
|
static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
|
|
|
|
{
|
|
|
|
char *newmode, *oldmode;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
2000-04-26 04:03:38 -04:00
|
|
|
g_return_if_fail(modestr != NULL);
|
|
|
|
|
2000-10-13 18:05:57 -04:00
|
|
|
newmode = modes_join(server->usermode, modestr, FALSE);
|
2000-04-26 04:03:38 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
static void event_mode(const char *data, IRC_SERVER_REC *server,
|
|
|
|
const char *nick)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-08-26 11:39:44 -04:00
|
|
|
IRC_CHANNEL_REC *chanrec;
|
2000-04-26 04:03:38 -04:00
|
|
|
char *params, *channel, *mode;
|
|
|
|
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
|
|
|
|
&channel, &mode);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
if (!ischannel(*channel)) {
|
|
|
|
/* user mode change */
|
|
|
|
parse_user_mode(server, mode);
|
|
|
|
} else {
|
|
|
|
/* channel mode change */
|
2000-08-26 11:39:44 -04:00
|
|
|
chanrec = irc_channel_find(server, channel);
|
2000-04-26 04:03:38 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel,
|
|
|
|
const char *nicks, const char *mode)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
|
|
|
GString *str;
|
|
|
|
int num, modepos;
|
|
|
|
char **nick, **nicklist;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
|
|
g_return_if_fail(channel != NULL && nicks != NULL && mode != NULL);
|
2000-04-26 04:03:38 -04:00
|
|
|
if (*nicks == '\0') return;
|
|
|
|
|
|
|
|
num = modepos = 0;
|
|
|
|
str = g_string_new(NULL);
|
|
|
|
|
|
|
|
nicklist = g_strsplit(nicks, " ", -1);
|
|
|
|
for (nick = nicklist; *nick != NULL; nick++) {
|
2000-06-30 15:44:50 -04:00
|
|
|
if (**nick == '\0')
|
2000-05-04 06:32:42 -04:00
|
|
|
continue;
|
|
|
|
|
2000-04-26 04:03:38 -04:00
|
|
|
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);
|
|
|
|
|
2000-06-12 19:33:35 -04:00
|
|
|
if (++num == server->max_modes_in_cmd) {
|
2000-04-26 04:03:38 -04:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
void channel_set_mode(IRC_SERVER_REC *server, const char *channel,
|
|
|
|
const char *mode)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-08-29 15:26:05 -04:00
|
|
|
char *modestr, *curmode, *orig, type, prevtype;
|
2000-04-26 04:03:38 -04:00
|
|
|
GString *tmode, *targs;
|
|
|
|
int count;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
|
|
g_return_if_fail(channel != NULL && mode != NULL);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
tmode = g_string_new(NULL);
|
|
|
|
targs = g_string_new(NULL);
|
2000-05-10 07:29:55 -04:00
|
|
|
count = 0;
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
orig = modestr = g_strdup(mode);
|
|
|
|
|
2000-08-29 15:26:05 -04:00
|
|
|
type = '+'; prevtype = '\0';
|
2000-04-26 04:03:38 -04:00
|
|
|
curmode = cmd_get_param(&modestr);
|
|
|
|
for (; *curmode != '\0'; curmode++) {
|
2000-08-29 15:26:05 -04:00
|
|
|
if (*curmode == '+' || *curmode == '-') {
|
2000-08-20 03:17:13 -04:00
|
|
|
type = *curmode;
|
2000-08-29 15:26:05 -04:00
|
|
|
continue;
|
|
|
|
}
|
2000-08-20 03:17:13 -04:00
|
|
|
|
|
|
|
if (count == server->max_modes_in_cmd &&
|
|
|
|
HAS_MODE_ARG(type, *curmode)) {
|
2000-08-26 11:39:44 -04:00
|
|
|
irc_send_cmdv(server, "MODE %s %s%s",
|
|
|
|
channel, tmode->str, targs->str);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-29 15:26:05 -04:00
|
|
|
count = 0; prevtype = '\0';
|
2000-04-26 04:03:38 -04:00
|
|
|
g_string_truncate(tmode, 0);
|
|
|
|
g_string_truncate(targs, 0);
|
|
|
|
}
|
|
|
|
|
2000-08-29 15:26:05 -04:00
|
|
|
if (type != prevtype) {
|
|
|
|
prevtype = type;
|
|
|
|
g_string_append_c(tmode, type);
|
|
|
|
}
|
2000-04-26 04:03:38 -04:00
|
|
|
g_string_append_c(tmode, *curmode);
|
|
|
|
|
2000-08-20 03:17:13 -04:00
|
|
|
if (HAS_MODE_ARG(type, *curmode)) {
|
2000-04-26 04:03:38 -04:00
|
|
|
char *arg;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
arg = cmd_get_param(&modestr);
|
|
|
|
if (*arg != '\0') g_string_sprintfa(targs, " %s", arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
if (tmode->len > 0) {
|
|
|
|
irc_send_cmdv(server, "MODE %s %s%s",
|
|
|
|
channel, tmode->str, targs->str);
|
|
|
|
}
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
g_string_free(tmode, TRUE);
|
|
|
|
g_string_free(targs, TRUE);
|
|
|
|
g_free(orig);
|
|
|
|
}
|
|
|
|
|
2000-10-22 11:25:17 -04:00
|
|
|
static void get_wildcard_nicks(GString *output, const char *mask,
|
|
|
|
IRC_CHANNEL_REC *channel, int op, int voice)
|
|
|
|
{
|
|
|
|
GSList *nicks, *tmp;
|
|
|
|
|
|
|
|
g_return_if_fail(output != NULL);
|
|
|
|
g_return_if_fail(mask != NULL);
|
|
|
|
g_return_if_fail(IS_IRC_CHANNEL(channel));
|
|
|
|
|
|
|
|
nicks = nicklist_find_multiple(CHANNEL(channel), mask);
|
|
|
|
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
|
|
|
|
NICK_REC *rec = tmp->data;
|
|
|
|
|
|
|
|
if ((op == 1 && !rec->op) || (op == 0 && rec->op) ||
|
|
|
|
(voice == 1 && !rec->voice) || (voice == 0 && rec->voice))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (g_strcasecmp(rec->nick, channel->server->nick) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
g_string_sprintfa(output, "%s ", rec->nick);
|
|
|
|
}
|
|
|
|
g_slist_free(nicks);
|
|
|
|
}
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
static char *get_nicks(IRC_CHANNEL_REC *channel,
|
|
|
|
const char *data, int op, int voice)
|
2000-05-04 06:32:42 -04:00
|
|
|
{
|
|
|
|
GString *str;
|
|
|
|
char **matches, **match, *ret;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
g_return_val_if_fail(channel != NULL, NULL);
|
2000-07-29 13:59:45 -04:00
|
|
|
g_return_val_if_fail(data != NULL, NULL);
|
|
|
|
if (*data == '\0') return NULL;
|
|
|
|
|
2000-05-04 06:32:42 -04:00
|
|
|
str = g_string_new(NULL);
|
|
|
|
matches = g_strsplit(data, " ", -1);
|
|
|
|
for (match = matches; *match != NULL; match++) {
|
2000-10-22 11:25:17 -04:00
|
|
|
if (strchr(*match, '*') == NULL &&
|
|
|
|
strchr(*match, '?') == NULL) {
|
2000-05-04 06:32:42 -04:00
|
|
|
/* no wildcards */
|
|
|
|
g_string_sprintfa(str, "%s ", *match);
|
2000-10-22 11:25:17 -04:00
|
|
|
} else {
|
|
|
|
get_wildcard_nicks(str, *match, channel, op, voice);
|
2000-05-04 06:32:42 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-09-10 15:28:56 -04:00
|
|
|
if (str->len > 0) g_string_truncate(str, str->len-1);
|
2000-05-04 06:32:42 -04:00
|
|
|
ret = str->str;
|
|
|
|
g_string_free(str, FALSE);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2000-07-23 19:19:22 -04:00
|
|
|
/* SYNTAX: OP <nicks> */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void cmd_op(const char *data, IRC_SERVER_REC *server,
|
|
|
|
IRC_CHANNEL_REC *channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-05-04 06:32:42 -04:00
|
|
|
char *nicks;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
if (!IS_IRC_CHANNEL(channel))
|
2000-05-04 06:32:42 -04:00
|
|
|
return;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
nicks = get_nicks(channel, data, 0, -1);
|
2000-07-29 13:59:45 -04:00
|
|
|
if (nicks != NULL && *nicks != '\0')
|
2000-08-26 11:39:44 -04:00
|
|
|
channel_set_singlemode(server, channel->name, nicks, "+o");
|
2000-07-29 13:59:45 -04:00
|
|
|
g_free_not_null(nicks);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-07-23 19:19:22 -04:00
|
|
|
/* SYNTAX: DEOP <nicks> */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void cmd_deop(const char *data, IRC_SERVER_REC *server,
|
|
|
|
IRC_CHANNEL_REC *channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-05-04 06:32:42 -04:00
|
|
|
char *nicks;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
if (!IS_IRC_CHANNEL(channel))
|
2000-05-04 06:32:42 -04:00
|
|
|
return;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
nicks = get_nicks(channel, data, 1, -1);
|
2000-07-29 13:59:45 -04:00
|
|
|
if (nicks != NULL && *nicks != '\0')
|
2000-08-26 11:39:44 -04:00
|
|
|
channel_set_singlemode(server, channel->name, nicks, "-o");
|
2000-07-29 13:59:45 -04:00
|
|
|
g_free_not_null(nicks);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-07-23 19:19:22 -04:00
|
|
|
/* SYNTAX: VOICE <nicks> */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void cmd_voice(const char *data, IRC_SERVER_REC *server,
|
|
|
|
IRC_CHANNEL_REC *channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-05-04 06:32:42 -04:00
|
|
|
char *nicks;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
if (!IS_IRC_CHANNEL(channel))
|
2000-05-04 06:32:42 -04:00
|
|
|
return;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
nicks = get_nicks(channel, data, 0, 0);
|
2000-07-29 13:59:45 -04:00
|
|
|
if (nicks != NULL && *nicks != '\0')
|
2000-08-26 11:39:44 -04:00
|
|
|
channel_set_singlemode(server, channel->name, nicks, "+v");
|
2000-07-29 13:59:45 -04:00
|
|
|
g_free_not_null(nicks);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-07-23 19:19:22 -04:00
|
|
|
/* SYNTAX: DEVOICE <nicks> */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void cmd_devoice(const char *data, IRC_SERVER_REC *server,
|
|
|
|
IRC_CHANNEL_REC *channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-05-04 06:32:42 -04:00
|
|
|
char *nicks;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
if (!IS_IRC_CHANNEL(channel))
|
2000-05-04 06:32:42 -04:00
|
|
|
return;
|
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
nicks = get_nicks(channel, data, 0, 1);
|
2000-07-29 13:59:45 -04:00
|
|
|
if (nicks != NULL && *nicks != '\0')
|
2000-08-26 11:39:44 -04:00
|
|
|
channel_set_singlemode(server, channel->name, nicks, "-v");
|
2000-07-29 13:59:45 -04:00
|
|
|
g_free_not_null(nicks);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
2000-07-23 19:19:22 -04:00
|
|
|
/* SYNTAX: MODE <your nick>|<channel> [<mode> [<mode parameters>]] */
|
2000-08-26 11:39:44 -04:00
|
|
|
static void cmd_mode(const char *data, IRC_SERVER_REC *server,
|
|
|
|
IRC_CHANNEL_REC *channel)
|
2000-04-26 04:03:38 -04:00
|
|
|
{
|
2000-06-17 21:18:12 -04:00
|
|
|
char *target, *mode;
|
|
|
|
void *free_arg;
|
2000-04-26 04:03:38 -04:00
|
|
|
|
|
|
|
g_return_if_fail(data != NULL);
|
2000-08-26 11:39:44 -04:00
|
|
|
if (server == NULL || !server->connected || !IS_IRC_SERVER(server))
|
2000-04-26 04:03:38 -04:00
|
|
|
cmd_return_error(CMDERR_NOT_CONNECTED);
|
|
|
|
|
2000-06-10 04:58:52 -04:00
|
|
|
if (*data == '+' || *data == '-') {
|
|
|
|
target = "*";
|
2000-06-17 21:18:12 -04:00
|
|
|
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
|
|
|
|
return;
|
2000-06-10 04:58:52 -04:00
|
|
|
} else {
|
2000-06-17 21:18:12 -04:00
|
|
|
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
|
|
|
|
return;
|
2000-06-10 04:58:52 -04:00
|
|
|
}
|
|
|
|
|
2000-04-26 04:03:38 -04:00
|
|
|
if (strcmp(target, "*") == 0) {
|
2000-08-26 11:39:44 -04:00
|
|
|
if (!IS_IRC_CHANNEL(channel))
|
2000-06-10 04:58:52 -04:00
|
|
|
cmd_param_error(CMDERR_NOT_JOINED);
|
2000-04-26 04:03:38 -04:00
|
|
|
|
2000-08-26 11:39:44 -04:00
|
|
|
target = channel->name;
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
|
|
|
|
|
2000-05-10 07:29:55 -04:00
|
|
|
if (*mode == '\0')
|
|
|
|
irc_send_cmdv(server, "MODE %s", target);
|
|
|
|
else if (ischannel(*target))
|
2000-04-26 04:03:38 -04:00
|
|
|
channel_set_mode(server, target, mode);
|
|
|
|
else
|
|
|
|
irc_send_cmdv(server, "MODE %s %s", target, mode);
|
|
|
|
|
2000-06-17 21:18:12 -04:00
|
|
|
cmd_params_free(free_arg);
|
2000-04-26 04:03:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|