Ejemplo n.º 1
0
/* SYNTAX: WHO <nicks>|<channels>|** */
static void cmd_who(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
	char *channel, *rest;
	void *free_arg;

	g_return_if_fail(data != NULL);
	if (!IS_IRC_SERVER(server) || !server->connected)
		cmd_return_error(CMDERR_NOT_CONNECTED);

	if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
		return;

	if (strcmp(channel, "*") == 0 || *channel == '\0') {
		if (!IS_IRC_CHANNEL(item))
                        cmd_param_error(CMDERR_NOT_JOINED);

		channel = item->name;
	}
	if (strcmp(channel, "**") == 0) {
		/* ** displays all nicks.. */
		*channel = '\0';
	}

	irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
		      channel, rest);
	cmd_params_free(free_arg);

	/* add default redirection */
	server_redirect_default((SERVER_REC *) server, "bogus command who");
}
Ejemplo n.º 2
0
int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode)
{
	g_return_val_if_fail(IS_IRC_CHANNEL(channel), FALSE);

	return channel->mode == NULL ? FALSE :
		mode_is_set(channel->mode, mode);
}
Ejemplo n.º 3
0
static void cmd_names(const char *data, IRC_SERVER_REC *server,
		      WI_ITEM_REC *item)
{
        GHashTable *optlist;
	char *channel;
	void *free_arg;

        CMD_IRC_SERVER(server);

	if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
			    PARAM_FLAG_GETREST, "names", &optlist, &channel))
		return;

	if (strcmp(channel, "*") == 0 || *channel == '\0') {
		if (!IS_IRC_CHANNEL(item))
                        cmd_param_error(CMDERR_NOT_JOINED);

		channel = IRC_CHANNEL(item)->name;
	}

	if (strcmp(channel, "**") == 0) {
		/* ** displays all nicks.. */
                irc_send_cmd(server, "NAMES");
	} else {
		irc_send_cmdv(server, "NAMES %s", channel);
	}

	cmd_params_free(free_arg);
}
Ejemplo n.º 4
0
/* SYNTAX: WHO [<nicks> | <channels> | **] */
static void cmd_who(const char *data, IRC_SERVER_REC *server,
		    WI_ITEM_REC *item)
{
	char *channel, *rest;
	void *free_arg;

        CMD_IRC_SERVER(server);

	if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
		return;

	if (strcmp(channel, "*") == 0 || *channel == '\0') {
		if (!IS_IRC_CHANNEL(item))
                        cmd_param_error(CMDERR_NOT_JOINED);

		channel = IRC_CHANNEL(item)->name;
	}
	if (strcmp(channel, "**") == 0) {
		/* ** displays all nicks.. */
		*channel = '\0';
	}

	irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
		      channel, rest);
	cmd_params_free(free_arg);
}
Ejemplo n.º 5
0
char *ban_get_mask(IRC_CHANNEL_REC *channel, const char *nick)
{
	NICK_REC *rec;
	char *str, *user, *host;

	g_return_val_if_fail(IS_IRC_CHANNEL(channel), NULL);
	g_return_val_if_fail(nick != NULL, NULL);

	rec = nicklist_find(CHANNEL(channel), nick);
	if (rec == NULL || rec->host == NULL) return NULL;

	str = irc_get_mask(nick, rec->host, bantype);

	/* there's a limit of 10 characters in user mask. so, banning
	   someone with user mask of 10 characters gives us "*1234567890",
	   which is one too much.. so, replace the 10th character with '*' */
	user = strchr(str, '!');
	if (user == NULL) return str;

	host = strchr(++user, '@');
	if (host == NULL) return str;

	if ((int) (host-user) > 10) {
		/* too long user mask */
		user[9] = '*';
		g_memmove(user+10, host, strlen(host)+1);
	}
	return str;
}
Ejemplo n.º 6
0
static void channel_destroyed(IRC_CHANNEL_REC *channel)
{
	if (!IS_IRC_CHANNEL(channel))
                return;

	banlist_free(channel->banlist);
}
Ejemplo n.º 7
0
/* Change nick's mode in channel */
static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *nick,
			     char mode, int type, const char *setby)
{
	NICK_REC *nickrec;
	char modestr[2], typestr[2];

	g_return_if_fail(IS_IRC_CHANNEL(channel));
	g_return_if_fail(nick != NULL);

	nickrec = nicklist_find(CHANNEL(channel), nick);
	if (nickrec == NULL) return; /* No /names list got yet */

	if (mode == '@') nickrec->op = type == '+';
	else if (mode == '+') nickrec->voice = type == '+';
	else if (mode == '%') nickrec->halfop = type == '+';
	if (channel->server->prefix[(unsigned char) mode] != '\0') {
		if (type == '+')
			prefix_add(nickrec->prefixes, mode, (SERVER_REC *) channel->server);
		else
			prefix_del(nickrec->prefixes, mode);
	}

	modestr[0] = mode; modestr[1] = '\0';
	typestr[0] = type; typestr[1] = '\0';
	signal_emit("nick mode changed", 5,
		    channel, nickrec, setby, modestr, typestr);
}
Ejemplo n.º 8
0
static int get_wildcard_nicks(GString *output, const char *mask,
			      IRC_CHANNEL_REC *channel, int op, int voice)
{
	GSList *nicks, *tmp;
        int count;

	g_return_val_if_fail(output != NULL, 0);
	g_return_val_if_fail(mask != NULL, 0);
	g_return_val_if_fail(IS_IRC_CHANNEL(channel), 0);

        count = 0;
	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);
                count++;
	}
	g_slist_free(nicks);

        return count;
}
Ejemplo n.º 9
0
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
{
	g_return_if_fail(channel != NULL);

	if (IS_IRC_CHANNEL(channel) && !channel->server->disconnected &&
	    !channel->synced)
		query_remove_all(channel);
}
Ejemplo n.º 10
0
/* expands to your usermode on channel, op '@', halfop '%', "+" voice */
static char *expando_cumode(SERVER_REC *server, void *item, int *free_ret)
{
	if (IS_IRC_CHANNEL(item) && CHANNEL(item)->ownnick) {
		return NICK(CHANNEL(item)->ownnick)->op ? "@" :
		       NICK(CHANNEL(item)->ownnick)->halfop ? "%" :
		       NICK(CHANNEL(item)->ownnick)->voice ? "+" : "";
	}
	return "";
}
Ejemplo n.º 11
0
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
{
	if (!IS_IRC_CHANNEL(channel))
                return;

	if (channel->server != NULL && !channel->left && !channel->kicked) {
		/* destroying channel record without actually
		   having left the channel yet */
		signal_emit("command part", 3, "", channel->server, channel);
	}
}
Ejemplo n.º 12
0
/* join ok/failed - remove from rejoins list. this happens always after join
   except if the "target unavailable" error happens again */
static void sig_remove_rejoin(IRC_CHANNEL_REC *channel)
{
	REJOIN_REC *rec;

	if (!IS_IRC_CHANNEL(channel) || channel->server == NULL)
		return;

	rec = rejoin_find(channel->server, channel->name);
	if (rec != NULL && rec->joining) {
		/* join failed, remove the rejoin */
		rejoin_destroy(channel->server, rec);
	}
}
Ejemplo n.º 13
0
/* SYNTAX: DEVOICE <nicks> */
static void cmd_devoice(const char *data, IRC_SERVER_REC *server,
			IRC_CHANNEL_REC *channel)
{
	char *nicks;

	if (!IS_IRC_CHANNEL(channel))
		return;

	nicks = get_nicks(channel, data, 0, 1);
	if (nicks != NULL && *nicks != '\0')
		channel_set_singlemode(server, channel->name, nicks, "-v");
	g_free_not_null(nicks);
}
Ejemplo n.º 14
0
static int channels_have_all_names(IRC_SERVER_REC *server)
{
	GSList *tmp;

	for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
		IRC_CHANNEL_REC *rec = tmp->data;

		if (IS_IRC_CHANNEL(rec) && !rec->names_got)
			return 0;
	}

	return 1;
}
Ejemplo n.º 15
0
/* expands to your usermode on channel, op '@', halfop '%', "+" voice or other */
static char *expando_cumode(SERVER_REC *server, void *item, int *free_ret)
{
	if (IS_IRC_CHANNEL(item) && CHANNEL(item)->ownnick) {
		char prefix = NICK(CHANNEL(item)->ownnick)->prefixes[0];
		if (prefix != '\0') {
			char *cumode = g_malloc(2);
			cumode[0] = prefix;
			cumode[1] = '\0';
			*free_ret = TRUE;
			return cumode;
		}
	}
	return "";
}
Ejemplo n.º 16
0
/* destroy all knockouts in channel */
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
{
	GSList *tmp, *next;

	if (!IS_IRC_CHANNEL(channel) || !IS_IRC_SERVER(channel->server))
		return;

	for (tmp = channel->server->knockoutlist; tmp != NULL; tmp = next) {
		KNOCKOUT_REC *rec = tmp->data;

		next = tmp->next;
		if (rec->channel == channel)
			knockout_destroy(channel->server, rec);
	}
}
Ejemplo n.º 17
0
/* Change nick's mode in channel */
static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *nick,
			     const char mode, int type)
{
	NICK_REC *nickrec;

	g_return_if_fail(IS_IRC_CHANNEL(channel));
	g_return_if_fail(nick != NULL);

	nickrec = nicklist_find(CHANNEL(channel), nick);
	if (nickrec == NULL) return; /* No /names list got yet */

	if (mode == '@') nickrec->op = type == '+';
	if (mode == '+') nickrec->voice = type == '+';
	if (mode == '%') nickrec->halfop = type == '+';

	signal_emit("nick mode changed", 2, channel, nickrec);
}
Ejemplo n.º 18
0
static void sig_channel_joined(IRC_CHANNEL_REC *channel)
{
	if (!IS_IRC_CHANNEL(channel))
		return;

	if (!settings_get_bool("channel_sync"))
		return;

	/* Add channel to query lists */
	if (!channel->no_modes)
		query_add_channel(channel, CHANNEL_QUERY_MODE);
	query_add_channel(channel, CHANNEL_QUERY_WHO);
	if (!channel->no_modes)
		query_add_channel(channel, CHANNEL_QUERY_BMODE);

	query_check(channel->server);
}
Ejemplo n.º 19
0
/* SYNTAX: MODE <your nick>|<channel> [<mode> [<mode parameters>]] */
static void cmd_mode(const char *data, IRC_SERVER_REC *server,
		     IRC_CHANNEL_REC *channel)
{
	IRC_CHANNEL_REC *chanrec;
	char *target, *mode;
	void *free_arg;

        CMD_IRC_SERVER(server);

	if (*data == '+' || *data == '-') {
		target = "*";
		if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
			return;
	} else {
		if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
			return;
	}

	if (strcmp(target, "*") == 0) {
		if (!IS_IRC_CHANNEL(channel))
			cmd_param_error(CMDERR_NOT_JOINED);

		target = channel->name;
	}
	if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);

	if (*mode == '\0') {
		chanrec = irc_channel_find(server, target);
		if (chanrec != NULL)
			target = chanrec->name;

		irc_send_cmdv(server, "MODE %s", target);
	} else if (ischannel(*target))
		channel_set_mode(server, target, mode);
	else {
		if (g_strcasecmp(target, server->nick) == 0) {
			server_redirect_event(server, "mode user", 1, target, -1, NULL,
					      "event mode", "requested usermode change", NULL);
		}

		irc_send_cmdv(server, "MODE %s %s", target, mode);
	}

	cmd_params_free(free_arg);
}
Ejemplo n.º 20
0
/* expands to your usermode on channel, op '@', halfop '%', "+" voice or other */
static char *expando_cumode(SERVER_REC *server, void *item, int *free_ret)
{
	if (IS_IRC_CHANNEL(item) && CHANNEL(item)->ownnick) {
		char other = NICK(CHANNEL(item)->ownnick)->other;
		if (other != '\0') {
			char *cumode = g_malloc(2);
			cumode[0] = other;
			cumode[1] = '\0';
			*free_ret = TRUE;
			return cumode;
		}

		return NICK(CHANNEL(item)->ownnick)->op ? "@" :
		       NICK(CHANNEL(item)->ownnick)->halfop ? "%" :
		       NICK(CHANNEL(item)->ownnick)->voice ? "+" : "";
	}
	return "";
}
Ejemplo n.º 21
0
/* SYNTAX: KNOCKOUT [<seconds>] <nick> <reason> */
static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
			 IRC_CHANNEL_REC *channel)
{
	KNOCKOUT_REC *rec;
	char *nick, *reason, *timeoutstr, *str;
	void *free_arg;
	int timeleft;

	g_return_if_fail(data != NULL);
	if (!IS_IRC_SERVER(server) || !server->connected)
		cmd_return_error(CMDERR_NOT_CONNECTED);
	if (!IS_IRC_CHANNEL(channel))
		cmd_return_error(CMDERR_NOT_JOINED);

	if (is_numeric(data, ' ')) {
		/* first argument is the timeout */
		if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST, &timeoutstr, &nick, &reason))
                        return;
		timeleft = atoi(timeoutstr);
	} else {
                timeleft = 0;
		if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nick, &reason))
			return;
	}

	if (timeleft == 0) timeleft = settings_get_int("knockout_time");
	if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);

	signal_emit("command ban", 3, nick, server, channel);

	str = g_strdup_printf("%s %s", nick, reason);
	signal_emit("command kick", 3, str, server, channel);
	g_free(str);

	/* create knockout record */
	rec = g_new(KNOCKOUT_REC, 1);
	rec->timeleft = timeleft;
	rec->channel = channel;
	rec->ban = ban_get_mask(channel, nick);

	server->knockoutlist = g_slist_append(server->knockoutlist, rec);

	cmd_params_free(free_arg);
}
Ejemplo n.º 22
0
void channel_set_singlemode(IRC_CHANNEL_REC *channel, const char *nicks,
			    const char *mode)
{
	GString *str;
	int num, modepos;
	char **nick, **nicklist;

	g_return_if_fail(IS_IRC_CHANNEL(channel));
	g_return_if_fail(nicks != NULL && 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 (**nick == '\0')
			continue;

		if (num == 0)
		{
			g_string_sprintf(str, "MODE %s %s",
					 channel->name, mode);
			modepos = str->len;
		} else {
			/* insert the mode string */
			g_string_insert(str, modepos, mode);
		}

		g_string_sprintfa(str, " %s", *nick);

		if (++num == channel->server->max_modes_in_cmd) {
			/* max. modes / command reached, send to server */
			irc_send_cmd(channel->server, str->str);
			num = 0;
		}
	}
	if (num > 0) irc_send_cmd(channel->server, str->str);

	g_strfreev(nicklist);
	g_string_free(str, TRUE);
}
Ejemplo n.º 23
0
/* SYNTAX: NAMES [-yes] [<channels>] */
static void cmd_names(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
	g_return_if_fail(data != NULL);

	if (!IS_IRC_SERVER(server) || !server->connected)
		cmd_return_error(CMDERR_NOT_CONNECTED);
	if (*data == '\0') cmd_return_error(CMDERR_NOT_GOOD_IDEA);

	if (strcmp(data, "*") == 0) {
		if (!IS_IRC_CHANNEL(item))
			cmd_return_error(CMDERR_NOT_JOINED);

		data = item->name;
	}

	if (g_strcasecmp(data, "-YES") == 0)
		irc_send_cmd(server, "NAMES");
	else
		irc_send_cmdv(server, "NAMES %s", data);
}
Ejemplo n.º 24
0
/* Find any unjoined channel that matches `channel'. Long channel names are
   also a bit problematic, so find a channel where start of the name matches. */
static IRC_CHANNEL_REC *channel_find_unjoined(IRC_SERVER_REC *server,
        const char *channel)
{
    GSList *tmp;
    int len;

    len = strlen(channel);
    for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
        IRC_CHANNEL_REC *rec = tmp->data;

        if (!IS_IRC_CHANNEL(rec) || rec->joined)
            continue;

        if (g_ascii_strncasecmp(channel, rec->name, len) == 0 &&
                (len > 20 || rec->name[len] == '\0'))
            return rec;
    }

    return NULL;
}
Ejemplo n.º 25
0
/* SYNTAX: INVITE <nick> [<channel>] */
static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
	char *nick, *channame;
	void *free_arg;

        CMD_IRC_SERVER(server);

	if (!cmd_get_params(data, &free_arg, 2, &nick, &channame))
		return;

	if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
	if (*channame == '\0' || strcmp(channame, "*") == 0) {
		if (!IS_IRC_CHANNEL(item))
			cmd_param_error(CMDERR_NOT_JOINED);

		channame = IRC_CHANNEL(item)->name;
	}

	irc_send_cmdv(server, "INVITE %s %s", nick, channame);
	cmd_params_free(free_arg);
}
Ejemplo n.º 26
0
static void server_check_massjoins(IRC_SERVER_REC *server, time_t max)
{
	GSList *tmp;

	/* Scan all channels through for massjoins */
	for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
		IRC_CHANNEL_REC *rec = tmp->data;

		if (!IS_IRC_CHANNEL(rec) || rec->massjoins <= 0)
			continue;

		if (rec->massjoin_start < max || /* We've waited long enough */
		    rec->massjoins-massjoin_max_joins < rec->last_massjoins) { /* Less than x joins since last check */
			/* send them */
			massjoin_send(rec);
		} else {
			/* Wait for some more.. */
			rec->last_massjoins = rec->massjoins;
		}
	}

}
Ejemplo n.º 27
0
Archivo: bans.c Proyecto: irssi/irssi
char *ban_get_mask(IRC_CHANNEL_REC *channel, const char *nick, int ban_type)
{
	NICK_REC *rec;
	char *str, *user, *host;
        int size;

	g_return_val_if_fail(IS_IRC_CHANNEL(channel), NULL);
	g_return_val_if_fail(nick != NULL, NULL);

	rec = nicklist_find(CHANNEL(channel), nick);
	if (rec == NULL) return NULL;
	if (rec->host == NULL) {
		g_warning("channel %s is not synced, using nick ban for %s", channel->name, nick);
		return g_strdup_printf("%s!*@*", nick);
	}

	if (ban_type <= 0)
		ban_type = default_ban_type;

	str = irc_get_mask(nick, rec->host, ban_type);

	/* there's a limit of 10 characters in user mask. so, banning
	   someone with user mask of 10 characters gives us "*1234567890",
	   which is one too much.. so, remove the first character after "*"
           so we'll get "*234567890" */
	user = strchr(str, '!');
	if (user == NULL) return str;

	host = strchr(++user, '@');
	if (host == NULL) return str;

        size = (int) (host-user);
	if (size >= 10) {
		/* too long user mask */
		g_memmove(user+1, user+(size-9), strlen(user+(size-9))+1);
	}
	return str;
}
Ejemplo n.º 28
0
/* SYNTAX: MODE <your nick>|<channel> [<mode> [<mode parameters>]] */
static void cmd_mode(const char *data, IRC_SERVER_REC *server,
		     IRC_CHANNEL_REC *channel)
{
	char *target, *mode;
	void *free_arg;

	g_return_if_fail(data != NULL);
	if (server == NULL || !server->connected || !IS_IRC_SERVER(server))
		cmd_return_error(CMDERR_NOT_CONNECTED);

	if (*data == '+' || *data == '-') {
		target = "*";
		if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
			return;
	} else {
		if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
			return;
	}

	if (strcmp(target, "*") == 0) {
		if (!IS_IRC_CHANNEL(channel))
			cmd_param_error(CMDERR_NOT_JOINED);

		target = channel->name;
	}
	if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);

	if (*mode == '\0')
		irc_send_cmdv(server, "MODE %s", target);
	else if (ischannel(*target))
		channel_set_mode(server, target, mode);
	else
		irc_send_cmdv(server, "MODE %s %s", target, mode);

	cmd_params_free(free_arg);
}
Ejemplo n.º 29
0
/* Add new nick to list */
NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
			      int op, int halfop, int voice, int send_massjoin,
			      const char *prefixes)
{
	NICK_REC *rec;

	g_return_val_if_fail(IS_IRC_CHANNEL(channel), NULL);
	g_return_val_if_fail(nick != NULL, NULL);

	rec = g_new0(NICK_REC, 1);
	rec->nick = g_strdup(nick);

	if (op) rec->op = TRUE;
	if (halfop) rec->halfop = TRUE;
	if (voice) rec->voice = TRUE;
	rec->send_massjoin = send_massjoin;

	if (prefixes != NULL) {
		g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
	}

	nicklist_insert(CHANNEL(channel), rec);
	return rec;
}
Ejemplo n.º 30
0
/* Parse channel mode string */
void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
			 const char *mode)
{
        GString *newmode;
	char *dup, *modestr, *arg, *curmode, type;

	g_return_if_fail(IS_IRC_CHANNEL(channel));
	g_return_if_fail(mode != NULL);

	type = '+';
	newmode = g_string_new(channel->mode);

	dup = modestr = g_strdup(mode);
	curmode = cmd_get_param(&modestr);
	while (*curmode != '\0') {
		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);
			if (*arg == '\0') {
				curmode++;
				continue;
			}
		} else {
			arg = NULL;
		}

		switch (*curmode) {
		case '+':
		case '-':
			type = *curmode;
			break;

		case 'b':
			if (type == '+')
				banlist_add(channel, arg, setby, time(NULL));
			else
				banlist_remove(channel, arg);
			break;
		case 'e':
			if (type == '+')
				banlist_exception_add(channel, arg, setby,
						      time(NULL));
			else
				banlist_exception_remove(channel, arg);
			break;
		case 'I':
			if (type == '+')
				invitelist_add(channel, arg);
			else
				invitelist_remove(channel, arg);
			break;

		case 'o':
			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);
			break;

		case 'l':
			mode_set_arg(newmode, type, 'l', arg);
			channel->limit = type == '-' ? 0 : atoi(arg);
			break;
		case 'k':
			mode_set_arg(newmode, type, 'k', arg);
			g_free_and_null(channel->key);
			if (type == '+')
				channel->key = g_strdup(arg);
			break;

		default:
                        mode_set(newmode, type, *curmode);
			break;
		}

		curmode++;
	}
	g_free(dup);

	if (strchr(channel->mode, 'k') == NULL && channel->key != NULL) {
		/* join was used with key but there's no key set
		   in channel modes.. */
		g_free(channel->key);
		channel->key = NULL;
	}

	if (strcmp(newmode->str, channel->mode) != 0) {
		g_free(channel->mode);
		channel->mode = g_strdup(newmode->str);

		signal_emit("channel mode changed", 1, channel);
	}

	g_string_free(newmode, TRUE);
}