2011-02-23 22:14:30 -05:00
|
|
|
/* X-Chat
|
|
|
|
* Copyright (C) 1998 Peter Zelezny.
|
|
|
|
*
|
|
|
|
* 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
|
2012-12-23 14:36:54 -05:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2011-02-23 22:14:30 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2012-10-24 15:33:02 -04:00
|
|
|
#include "hexchat.h"
|
|
|
|
#include "hexchatc.h"
|
2011-02-23 22:14:30 -05:00
|
|
|
#include "modes.h"
|
|
|
|
#include "server.h"
|
|
|
|
#include "text.h"
|
|
|
|
#include "fe.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "inbound.h"
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
#include <strings.h>
|
|
|
|
#endif
|
|
|
|
|
2013-04-02 23:39:37 -04:00
|
|
|
#include <glib/gprintf.h>
|
|
|
|
|
2011-02-23 22:14:30 -05:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
server *serv;
|
|
|
|
char *op;
|
|
|
|
char *deop;
|
|
|
|
char *voice;
|
|
|
|
char *devoice;
|
|
|
|
} mode_run;
|
|
|
|
|
|
|
|
static int is_prefix_char (server * serv, char c);
|
|
|
|
static void record_chan_mode (session *sess, char sign, char mode, char *arg);
|
|
|
|
static char *mode_cat (char *str, char *addition);
|
2013-06-22 10:32:50 -04:00
|
|
|
static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
|
|
|
|
char *chan, char *arg, int quiet, int is_324,
|
|
|
|
const message_tags_data *tags_data);
|
2011-02-23 22:14:30 -05:00
|
|
|
static int mode_has_arg (server *serv, char sign, char mode);
|
2013-06-22 10:32:50 -04:00
|
|
|
static void mode_print_grouped (session *sess, char *nick, mode_run *mr,
|
|
|
|
const message_tags_data *tags_data);
|
2011-02-23 22:14:30 -05:00
|
|
|
static int mode_chanmode_type (server * serv, char mode);
|
|
|
|
|
|
|
|
|
|
|
|
/* word[] - list of nicks.
|
|
|
|
wpos - index into word[]. Where nicks really start.
|
|
|
|
end - index into word[]. Last entry plus one.
|
|
|
|
sign - a char, e.g. '+' or '-'
|
|
|
|
mode - a mode, e.g. 'o' or 'v' */
|
|
|
|
void
|
|
|
|
send_channel_modes (session *sess, char *tbuf, char *word[], int wpos,
|
|
|
|
int end, char sign, char mode, int modes_per_line)
|
|
|
|
{
|
|
|
|
int usable_modes, orig_len, len, wlen, i, max;
|
|
|
|
server *serv = sess->server;
|
|
|
|
|
|
|
|
/* sanity check. IRC RFC says three per line. */
|
|
|
|
if (serv->modes_per_line < 3)
|
|
|
|
serv->modes_per_line = 3;
|
|
|
|
if (modes_per_line < 1)
|
|
|
|
modes_per_line = serv->modes_per_line;
|
|
|
|
|
|
|
|
/* RFC max, minus length of "MODE %s " and "\r\n" and 1 +/- sign */
|
|
|
|
/* 512 - 6 - 2 - 1 - strlen(chan) */
|
|
|
|
max = 503 - strlen (sess->channel);
|
|
|
|
|
|
|
|
while (wpos < end)
|
|
|
|
{
|
|
|
|
tbuf[0] = '\0';
|
|
|
|
orig_len = len = 0;
|
|
|
|
|
|
|
|
/* we'll need this many modechars too */
|
|
|
|
len += modes_per_line;
|
|
|
|
|
|
|
|
/* how many can we fit? */
|
|
|
|
for (i = 0; i < modes_per_line; i++)
|
|
|
|
{
|
|
|
|
/* no more nicks left? */
|
|
|
|
if (wpos + i >= end)
|
|
|
|
break;
|
|
|
|
wlen = strlen (word[wpos + i]) + 1;
|
|
|
|
if (wlen + len > max)
|
|
|
|
break;
|
|
|
|
len += wlen; /* length of our whole string so far */
|
|
|
|
}
|
|
|
|
if (i < 1)
|
|
|
|
return;
|
|
|
|
usable_modes = i; /* this is how many we'll send on this line */
|
|
|
|
|
|
|
|
/* add the +/-modemodemodemode */
|
|
|
|
len = orig_len;
|
|
|
|
tbuf[len] = sign;
|
|
|
|
len++;
|
|
|
|
for (i = 0; i < usable_modes; i++)
|
|
|
|
{
|
|
|
|
tbuf[len] = mode;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
tbuf[len] = 0; /* null terminate for the strcat() to work */
|
|
|
|
|
|
|
|
/* add all the nicknames */
|
|
|
|
for (i = 0; i < usable_modes; i++)
|
|
|
|
{
|
|
|
|
strcat (tbuf, " ");
|
|
|
|
strcat (tbuf, word[wpos + i]);
|
|
|
|
}
|
|
|
|
serv->p_mode (serv, sess->channel, tbuf);
|
|
|
|
|
|
|
|
wpos += usable_modes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* does 'chan' have a valid prefix? e.g. # or & */
|
|
|
|
|
|
|
|
int
|
|
|
|
is_channel (server * serv, char *chan)
|
|
|
|
{
|
|
|
|
if (strchr (serv->chantypes, chan[0]))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is the given char a valid nick mode char? e.g. @ or + */
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_prefix_char (server * serv, char c)
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
char *np = serv->nick_prefixes;
|
|
|
|
|
|
|
|
while (np[0])
|
|
|
|
{
|
|
|
|
if (np[0] == c)
|
|
|
|
return pos;
|
|
|
|
pos++;
|
|
|
|
np++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (serv->bad_prefix)
|
|
|
|
{
|
|
|
|
if (strchr (serv->bad_nick_prefixes, c))
|
|
|
|
/* valid prefix char, but mode unknown */
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns '@' for ops etc... */
|
|
|
|
|
|
|
|
char
|
|
|
|
get_nick_prefix (server * serv, unsigned int access)
|
|
|
|
{
|
|
|
|
int pos;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
for (pos = 0; pos < USERACCESS_SIZE; pos++)
|
|
|
|
{
|
|
|
|
c = serv->nick_prefixes[pos];
|
|
|
|
if (c == 0)
|
|
|
|
break;
|
|
|
|
if (access & (1 << pos))
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns the access bitfield for a nickname. E.g.
|
|
|
|
@nick would return 000010 in binary
|
|
|
|
%nick would return 000100 in binary
|
|
|
|
+nick would return 001000 in binary */
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
nick_access (server * serv, char *nick, int *modechars)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned int access = 0;
|
|
|
|
char *orig = nick;
|
|
|
|
|
|
|
|
while (*nick)
|
|
|
|
{
|
|
|
|
i = is_prefix_char (serv, *nick);
|
|
|
|
if (i == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -2 == valid prefix char, but mode unknown */
|
|
|
|
if (i != -2)
|
|
|
|
access |= (1 << i);
|
|
|
|
|
|
|
|
nick++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*modechars = nick - orig;
|
|
|
|
|
|
|
|
return access;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns the access number for a particular mode. e.g.
|
|
|
|
mode 'a' returns 0
|
|
|
|
mode 'o' returns 1
|
|
|
|
mode 'h' returns 2
|
|
|
|
mode 'v' returns 3
|
|
|
|
Also puts the nick-prefix-char in 'prefix' */
|
|
|
|
|
|
|
|
int
|
|
|
|
mode_access (server * serv, char mode, char *prefix)
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
|
|
|
|
while (serv->nick_modes[pos])
|
|
|
|
{
|
|
|
|
if (serv->nick_modes[pos] == mode)
|
|
|
|
{
|
|
|
|
*prefix = serv->nick_prefixes[pos];
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*prefix = 0;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
record_chan_mode (session *sess, char sign, char mode, char *arg)
|
|
|
|
{
|
|
|
|
/* Somebody needed to acutally update sess->current_modes, needed to
|
|
|
|
play nice with bouncers, and less mode calls. Also keeps modes up
|
|
|
|
to date for scripts */
|
|
|
|
server *serv = sess->server;
|
|
|
|
GString *current = g_string_new(sess->current_modes);
|
|
|
|
gint mode_pos = -1;
|
|
|
|
gchar *current_char = current->str;
|
|
|
|
gint modes_length;
|
|
|
|
gint argument_num = 0;
|
|
|
|
gint argument_offset = 0;
|
|
|
|
gint argument_length = 0;
|
|
|
|
int i = 0;
|
|
|
|
gchar *arguments_start;
|
|
|
|
|
|
|
|
/* find out if the mode currently exists */
|
|
|
|
arguments_start = g_strstr_len(current->str , -1, " ");
|
|
|
|
if (arguments_start) {
|
|
|
|
modes_length = arguments_start - current->str;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
modes_length = current->len;
|
|
|
|
/* set this to the end of the modes */
|
|
|
|
arguments_start = current->str + current->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (mode_pos == -1 && i < modes_length)
|
|
|
|
{
|
|
|
|
if (*current_char == mode)
|
|
|
|
{
|
|
|
|
mode_pos = i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
current_char++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if the mode currently exists and has an arg, need to know where
|
|
|
|
* (including leading space) */
|
|
|
|
if (mode_pos != -1 && mode_has_arg(serv, '+', mode))
|
|
|
|
{
|
|
|
|
current_char = current->str;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (i <= mode_pos)
|
|
|
|
{
|
|
|
|
if (mode_has_arg(serv, '+', *current_char))
|
|
|
|
argument_num++;
|
|
|
|
current_char++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check through arguments for where to start */
|
|
|
|
current_char = arguments_start;
|
|
|
|
i = 0;
|
|
|
|
while (i < argument_num && *current_char != '\0')
|
|
|
|
{
|
|
|
|
if (*current_char == ' ')
|
|
|
|
i++;
|
|
|
|
if (i != argument_num)
|
|
|
|
current_char++;
|
|
|
|
}
|
|
|
|
argument_offset = current_char - current->str;
|
|
|
|
|
|
|
|
/* how long the existing argument is for this key
|
|
|
|
* important for malloc and strncpy */
|
|
|
|
if (i == argument_num)
|
|
|
|
{
|
|
|
|
argument_length++;
|
|
|
|
current_char++;
|
|
|
|
while (*current_char != '\0' && *current_char != ' ')
|
|
|
|
{
|
|
|
|
argument_length++;
|
|
|
|
current_char++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* two cases, adding and removing a mode, handled differently */
|
|
|
|
if (sign == '+')
|
|
|
|
{
|
|
|
|
if (mode_pos != -1)
|
|
|
|
{
|
|
|
|
/* if it already exists, only need to do something (change)
|
|
|
|
* if there should be a param */
|
|
|
|
if (mode_has_arg(serv, sign, mode))
|
|
|
|
{
|
|
|
|
/* leave the old space there */
|
|
|
|
current = g_string_erase(current, argument_offset+1, argument_length-1);
|
|
|
|
current = g_string_insert(current, argument_offset+1, arg);
|
|
|
|
|
|
|
|
free(sess->current_modes);
|
|
|
|
sess->current_modes = g_string_free(current, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* mode wasn't there before */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* insert the new mode character */
|
|
|
|
current = g_string_insert_c(current, modes_length, mode);
|
|
|
|
|
|
|
|
/* add the argument, with space if there is one */
|
|
|
|
if (mode_has_arg(serv, sign, mode))
|
|
|
|
{
|
|
|
|
current = g_string_append_c(current, ' ');
|
|
|
|
current = g_string_append(current, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(sess->current_modes);
|
|
|
|
sess->current_modes = g_string_free(current, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (sign == '-' && mode_pos != -1)
|
|
|
|
{
|
|
|
|
/* remove the argument first if it has one*/
|
|
|
|
if (mode_has_arg(serv, '+', mode))
|
|
|
|
current = g_string_erase(current, argument_offset, argument_length);
|
|
|
|
|
|
|
|
/* remove the mode character */
|
|
|
|
current = g_string_erase(current, mode_pos, 1);
|
|
|
|
|
|
|
|
free(sess->current_modes);
|
|
|
|
sess->current_modes = g_string_free(current, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
mode_cat (char *str, char *addition)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (str)
|
|
|
|
{
|
|
|
|
len = strlen (str) + strlen (addition) + 2;
|
|
|
|
str = realloc (str, len);
|
|
|
|
strcat (str, " ");
|
|
|
|
strcat (str, addition);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
str = strdup (addition);
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle one mode, e.g.
|
|
|
|
handle_single_mode (mr,'+','b',"elite","#warez","banneduser",) */
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
|
2013-06-22 10:32:50 -04:00
|
|
|
char *chan, char *arg, int quiet, int is_324,
|
|
|
|
const message_tags_data *tags_data)
|
2011-02-23 22:14:30 -05:00
|
|
|
{
|
|
|
|
session *sess;
|
|
|
|
server *serv = mr->serv;
|
|
|
|
char outbuf[4];
|
2013-04-07 17:49:30 -04:00
|
|
|
char *cm = serv->chanmodes;
|
|
|
|
gboolean supportsq = FALSE;
|
2011-02-23 22:14:30 -05:00
|
|
|
|
|
|
|
outbuf[0] = sign;
|
|
|
|
outbuf[1] = 0;
|
|
|
|
outbuf[2] = mode;
|
|
|
|
outbuf[3] = 0;
|
|
|
|
|
|
|
|
sess = find_channel (serv, chan);
|
|
|
|
if (!sess || !is_channel (serv, chan))
|
|
|
|
{
|
|
|
|
/* got modes for a chan we're not in! probably nickmode +isw etc */
|
|
|
|
sess = serv->front_session;
|
|
|
|
goto genmode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is this a nick mode? */
|
|
|
|
if (strchr (serv->nick_modes, mode))
|
|
|
|
{
|
|
|
|
/* update the user in the userlist */
|
|
|
|
userlist_update_mode (sess, /*nickname */ arg, mode, sign);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
if (!is_324 && !sess->ignore_mode && mode_chanmode_type(serv, mode) >= 1)
|
|
|
|
record_chan_mode (sess, sign, mode, arg);
|
|
|
|
}
|
|
|
|
|
2013-04-07 17:49:30 -04:00
|
|
|
/* Is q a chanmode on this server? */
|
|
|
|
if (cm)
|
|
|
|
while (*cm)
|
|
|
|
{
|
|
|
|
if (*cm == ',')
|
|
|
|
break;
|
|
|
|
if (*cm == 'q')
|
|
|
|
supportsq = TRUE;
|
|
|
|
cm++;
|
|
|
|
}
|
|
|
|
|
2011-02-23 22:14:30 -05:00
|
|
|
switch (sign)
|
|
|
|
{
|
|
|
|
case '+':
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case 'k':
|
|
|
|
safe_strcpy (sess->channelkey, arg, sizeof (sess->channelkey));
|
|
|
|
fe_update_channel_key (sess);
|
|
|
|
fe_update_mode_buttons (sess, mode, sign);
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETKEY, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'l':
|
|
|
|
sess->limit = atoi (arg);
|
|
|
|
fe_update_channel_limit (sess);
|
|
|
|
fe_update_mode_buttons (sess, mode, sign);
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'o':
|
|
|
|
if (!quiet)
|
|
|
|
mr->op = mode_cat (mr->op, arg);
|
|
|
|
return;
|
|
|
|
case 'h':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'v':
|
|
|
|
if (!quiet)
|
|
|
|
mr->voice = mode_cat (mr->voice, arg);
|
|
|
|
return;
|
|
|
|
case 'b':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'e':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANEXEMPT, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'I':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
2013-04-07 17:49:30 -04:00
|
|
|
case 'q':
|
|
|
|
if (!supportsq)
|
|
|
|
break; /* +q is owner on this server */
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANQUIET, sess, nick, arg, NULL, NULL, 0,
|
|
|
|
tags_data->timestamp);
|
2013-04-07 17:49:30 -04:00
|
|
|
return;
|
2011-02-23 22:14:30 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case 'k':
|
|
|
|
sess->channelkey[0] = 0;
|
|
|
|
fe_update_channel_key (sess);
|
|
|
|
fe_update_mode_buttons (sess, mode, sign);
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMKEY, sess, nick, NULL, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'l':
|
|
|
|
sess->limit = 0;
|
|
|
|
fe_update_channel_limit (sess);
|
|
|
|
fe_update_mode_buttons (sess, mode, sign);
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'o':
|
|
|
|
if (!quiet)
|
|
|
|
mr->deop = mode_cat (mr->deop, arg);
|
|
|
|
return;
|
|
|
|
case 'h':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEHOP, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'v':
|
|
|
|
if (!quiet)
|
|
|
|
mr->devoice = mode_cat (mr->devoice, arg);
|
|
|
|
return;
|
|
|
|
case 'b':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'e':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
|
|
|
case 'I':
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMINVITE, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
return;
|
2013-04-07 17:49:30 -04:00
|
|
|
case 'q':
|
|
|
|
if (!supportsq)
|
|
|
|
break; /* -q is owner on this server */
|
|
|
|
if (!quiet)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNQUIET, sess, nick, arg, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2013-04-07 17:49:30 -04:00
|
|
|
return;
|
2011-02-23 22:14:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fe_update_mode_buttons (sess, mode, sign);
|
|
|
|
|
|
|
|
genmode:
|
|
|
|
/* Received umode +e. If we're waiting to send JOIN then send now! */
|
|
|
|
if (mode == 'e' && sign == '+' && !serv->p_cmp (chan, serv->nick))
|
|
|
|
inbound_identified (serv);
|
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (*arg)
|
|
|
|
{
|
|
|
|
char *buf = malloc (strlen (chan) + strlen (arg) + 2);
|
|
|
|
sprintf (buf, "%s %s", chan, arg);
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
|
|
|
|
outbuf + 2, buf, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
free (buf);
|
|
|
|
} else
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
|
|
|
|
outbuf + 2, chan, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* does this mode have an arg? like +b +l +o */
|
|
|
|
|
|
|
|
static int
|
|
|
|
mode_has_arg (server * serv, char sign, char mode)
|
|
|
|
{
|
|
|
|
int type;
|
|
|
|
|
|
|
|
/* if it's a nickmode, it must have an arg */
|
|
|
|
if (strchr (serv->nick_modes, mode))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
type = mode_chanmode_type (serv, mode);
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case 0: /* type A */
|
|
|
|
case 1: /* type B */
|
|
|
|
return 1;
|
|
|
|
case 2: /* type C */
|
|
|
|
if (sign == '+')
|
|
|
|
return 1;
|
|
|
|
case 3: /* type D */
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* what type of chanmode is it? -1 for not in chanmode */
|
|
|
|
static int
|
|
|
|
mode_chanmode_type (server * serv, char mode)
|
|
|
|
{
|
|
|
|
/* see what numeric 005 CHANMODES=xxx said */
|
|
|
|
char *cm = serv->chanmodes;
|
|
|
|
int type = 0;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
while (*cm && !found)
|
|
|
|
{
|
|
|
|
if (*cm == ',')
|
|
|
|
{
|
|
|
|
type++;
|
|
|
|
} else if (*cm == mode)
|
|
|
|
{
|
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
cm++;
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
return type;
|
|
|
|
/* not found? -1 */
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-06-22 10:32:50 -04:00
|
|
|
mode_print_grouped (session *sess, char *nick, mode_run *mr,
|
|
|
|
const message_tags_data *tags_data)
|
2011-02-23 22:14:30 -05:00
|
|
|
{
|
|
|
|
/* print all the grouped Op/Deops */
|
|
|
|
if (mr->op)
|
|
|
|
{
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0,
|
|
|
|
tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
free (mr->op);
|
|
|
|
mr->op = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mr->deop)
|
|
|
|
{
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
free (mr->deop);
|
|
|
|
mr->deop = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mr->voice)
|
|
|
|
{
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL,
|
|
|
|
0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
free (mr->voice);
|
|
|
|
mr->voice = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mr->devoice)
|
|
|
|
{
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL,
|
|
|
|
NULL, 0, tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
free (mr->devoice);
|
|
|
|
mr->devoice = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handle a MODE or numeric 324 from server */
|
|
|
|
|
|
|
|
void
|
|
|
|
handle_mode (server * serv, char *word[], char *word_eol[],
|
2013-06-22 10:32:50 -04:00
|
|
|
char *nick, int numeric_324, const message_tags_data *tags_data)
|
2011-02-23 22:14:30 -05:00
|
|
|
{
|
|
|
|
session *sess;
|
|
|
|
char *chan;
|
|
|
|
char *modes;
|
|
|
|
char *argstr;
|
|
|
|
char sign;
|
|
|
|
int len;
|
|
|
|
int arg;
|
|
|
|
int i, num_args;
|
|
|
|
int num_modes;
|
|
|
|
int offset = 3;
|
|
|
|
int all_modes_have_args = FALSE;
|
|
|
|
int using_front_tab = FALSE;
|
|
|
|
mode_run mr;
|
|
|
|
|
|
|
|
mr.serv = serv;
|
|
|
|
mr.op = mr.deop = mr.voice = mr.devoice = NULL;
|
|
|
|
|
|
|
|
/* numeric 324 has everything 1 word later (as opposed to MODE) */
|
|
|
|
if (numeric_324)
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
chan = word[offset];
|
|
|
|
modes = word[offset + 1];
|
|
|
|
if (*modes == ':')
|
|
|
|
modes++;
|
|
|
|
|
|
|
|
if (*modes == 0)
|
|
|
|
return; /* beyondirc's blank modes */
|
|
|
|
|
|
|
|
sess = find_channel (serv, chan);
|
|
|
|
if (!sess)
|
|
|
|
{
|
|
|
|
sess = serv->front_session;
|
|
|
|
using_front_tab = TRUE;
|
|
|
|
}
|
|
|
|
/* remove trailing space */
|
|
|
|
len = strlen (word_eol[offset]) - 1;
|
|
|
|
if (word_eol[offset][len] == ' ')
|
|
|
|
word_eol[offset][len] = 0;
|
|
|
|
|
2012-10-22 08:50:36 -04:00
|
|
|
if (prefs.hex_irc_raw_modes && !numeric_324)
|
2013-06-22 10:32:50 -04:00
|
|
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0,
|
|
|
|
tags_data->timestamp);
|
2011-02-23 22:14:30 -05:00
|
|
|
|
|
|
|
if (numeric_324 && !using_front_tab)
|
|
|
|
{
|
|
|
|
if (sess->current_modes)
|
|
|
|
free (sess->current_modes);
|
|
|
|
sess->current_modes = strdup (word_eol[offset+1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
sign = *modes;
|
|
|
|
modes++;
|
|
|
|
arg = 1;
|
|
|
|
|
|
|
|
/* count the number of arguments (e.g. after the -o+v) */
|
|
|
|
num_args = 0;
|
|
|
|
i = 1;
|
|
|
|
while ((i + offset + 1) < PDIWORDS)
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
if (!(*word[i + offset]))
|
|
|
|
break;
|
|
|
|
num_args++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* count the number of modes (without the -/+ chars */
|
|
|
|
num_modes = 0;
|
|
|
|
i = 0;
|
|
|
|
while (i < strlen (modes))
|
|
|
|
{
|
|
|
|
if (modes[i] != '+' && modes[i] != '-')
|
|
|
|
num_modes++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_args == num_modes)
|
|
|
|
all_modes_have_args = TRUE;
|
|
|
|
|
|
|
|
while (*modes)
|
|
|
|
{
|
|
|
|
switch (*modes)
|
|
|
|
{
|
|
|
|
case '-':
|
|
|
|
case '+':
|
|
|
|
/* print all the grouped Op/Deops */
|
2013-06-22 10:32:50 -04:00
|
|
|
mode_print_grouped (sess, nick, &mr, tags_data);
|
2011-02-23 22:14:30 -05:00
|
|
|
sign = *modes;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
argstr = "";
|
|
|
|
if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args+1))
|
|
|
|
{
|
|
|
|
arg++;
|
|
|
|
argstr = word[arg + offset];
|
|
|
|
}
|
|
|
|
handle_single_mode (&mr, sign, *modes, nick, chan,
|
2012-10-22 08:50:36 -04:00
|
|
|
argstr, numeric_324 || prefs.hex_irc_raw_modes,
|
2013-06-22 10:32:50 -04:00
|
|
|
numeric_324, tags_data);
|
2011-02-23 22:14:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
modes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the title at the end, now that the mode update is internal now */
|
|
|
|
if (!using_front_tab)
|
|
|
|
fe_set_title (sess);
|
|
|
|
|
|
|
|
/* print all the grouped Op/Deops */
|
2013-06-22 10:32:50 -04:00
|
|
|
mode_print_grouped (sess, nick, &mr, tags_data);
|
2011-02-23 22:14:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* handle the 005 numeric */
|
|
|
|
|
|
|
|
void
|
2013-06-22 18:48:17 -04:00
|
|
|
inbound_005 (server * serv, char *word[], const message_tags_data *tags_data)
|
2011-02-23 22:14:30 -05:00
|
|
|
{
|
|
|
|
int w;
|
|
|
|
char *pre;
|
|
|
|
|
|
|
|
w = 4; /* start at the 4th word */
|
|
|
|
while (w < PDIWORDS && *word[w])
|
|
|
|
{
|
|
|
|
if (strncmp (word[w], "MODES=", 6) == 0)
|
|
|
|
{
|
|
|
|
serv->modes_per_line = atoi (word[w] + 6);
|
|
|
|
} else if (strncmp (word[w], "CHANTYPES=", 10) == 0)
|
|
|
|
{
|
|
|
|
free (serv->chantypes);
|
|
|
|
serv->chantypes = strdup (word[w] + 10);
|
|
|
|
} else if (strncmp (word[w], "CHANMODES=", 10) == 0)
|
|
|
|
{
|
|
|
|
free (serv->chanmodes);
|
|
|
|
serv->chanmodes = strdup (word[w] + 10);
|
|
|
|
} else if (strncmp (word[w], "PREFIX=", 7) == 0)
|
|
|
|
{
|
|
|
|
pre = strchr (word[w] + 7, ')');
|
|
|
|
if (pre)
|
|
|
|
{
|
|
|
|
pre[0] = 0; /* NULL out the ')' */
|
|
|
|
free (serv->nick_prefixes);
|
|
|
|
free (serv->nick_modes);
|
|
|
|
serv->nick_prefixes = strdup (pre + 1);
|
|
|
|
serv->nick_modes = strdup (word[w] + 8);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
/* bad! some ircds don't give us the modes. */
|
|
|
|
/* in this case, we use it only to strip /NAMES */
|
|
|
|
serv->bad_prefix = TRUE;
|
|
|
|
if (serv->bad_nick_prefixes)
|
|
|
|
free (serv->bad_nick_prefixes);
|
|
|
|
serv->bad_nick_prefixes = strdup (word[w] + 7);
|
|
|
|
}
|
|
|
|
} else if (strncmp (word[w], "WATCH=", 6) == 0)
|
|
|
|
{
|
|
|
|
serv->supports_watch = TRUE;
|
2013-04-03 18:51:54 -04:00
|
|
|
} else if (strncmp (word[w], "MONITOR=", 8) == 0)
|
|
|
|
{
|
|
|
|
serv->supports_monitor = TRUE;
|
2011-02-23 22:14:30 -05:00
|
|
|
} else if (strncmp (word[w], "NETWORK=", 8) == 0)
|
|
|
|
{
|
|
|
|
/* if (serv->networkname)
|
|
|
|
free (serv->networkname);
|
|
|
|
serv->networkname = strdup (word[w] + 8);*/
|
|
|
|
|
|
|
|
if (serv->server_session->type == SESS_SERVER)
|
|
|
|
{
|
|
|
|
safe_strcpy (serv->server_session->channel, word[w] + 8, CHANLEN);
|
|
|
|
fe_set_channel (serv->server_session);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (strncmp (word[w], "CASEMAPPING=", 12) == 0)
|
|
|
|
{
|
|
|
|
if (strcmp (word[w] + 12, "ascii") == 0) /* bahamut */
|
2012-06-16 07:01:47 -04:00
|
|
|
serv->p_cmp = (void *)g_ascii_strcasecmp;
|
2011-02-23 22:14:30 -05:00
|
|
|
} else if (strncmp (word[w], "CHARSET=", 8) == 0)
|
|
|
|
{
|
2013-09-06 14:00:53 -04:00
|
|
|
if (g_ascii_strncasecmp (word[w] + 8, "UTF-8", 5) == 0)
|
2011-02-23 22:14:30 -05:00
|
|
|
{
|
|
|
|
server_set_encoding (serv, "UTF-8");
|
|
|
|
}
|
|
|
|
} else if (strcmp (word[w], "NAMESX") == 0)
|
|
|
|
{
|
|
|
|
/* 12345678901234567 */
|
|
|
|
tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17);
|
|
|
|
} else if (strcmp (word[w], "WHOX") == 0)
|
|
|
|
{
|
|
|
|
serv->have_whox = TRUE;
|
|
|
|
} else if (strcmp (word[w], "EXCEPTS") == 0)
|
|
|
|
{
|
|
|
|
#ifndef WIN32
|
|
|
|
serv->have_except = TRUE;
|
|
|
|
#endif
|
Redesign the Ban List window. Closes Issues #303, #342, #427
This is a combination of 18 commits.
The first commit's message is:
Here is the initial banlist branch of RichardHitt/hexchat.
Changed files are only src/fe-gtk/{banlist.c,fe-gtk.h}.
This version works and contains my first efforts at selective
sensitization of radio buttons and control buttons.
From this point I intend to undertake a stepwise redesign.
Step 1 will be to make the existing banlist code work for
multiple simultaneous banlist windows (for different channels,
obviously). It will be a hackathon with the only goal of getting
it working.
Step 2 will be the objectization and alpha-stage tidying-up of
all the terrible looking stuff I will have done in Step 1.
This is the 2nd commit message:
Here's the post-Step-1 commit. It works for multiple banlist windows.
Note particularly what I've done to banlist.h.
Note that for many functions in banlist.c the argument is now
a banlist_info *, rather than a session *.
Note in banlist.c the initialization of array modes[] which
contains driving information for the checkboxes.
Of course those checkboxes aren't yet implemented. Maybe in Step 2
I will change to checkboxes from radio buttons; but definitely I will
change to letting modes[] drive processing.
This is the 3rd commit message:
Converted to checkboxes. Much additional work.
Note that the infrastructure for Auto-invite is not yet
present in the hexchat tree. I'm nearly done with banlist,
I think!
This is the 4th commit message:
Fleshed out 'invite'. Tagged masks uniformly, e.g. (b) (e) (I).
General cleanup, nearly at the point of beta quality.
This is the 5th commit message:
Added fourth mode type: quiet. Did lots and lots of cleanup. Beta-ready?
This is the 6th commit message:
Get the banlist timestamps properly sortable.
This is the 7th commit message:
Redesign the supports_foo() routines. Now they're responsible for setting
the flags in ->capable, ->readable, ->writeable.
This is the 8th commit message:
Deleted a couple of RBH comments.
This is the 9th commit message:
Now the ESC key will close the banlist window.
This is the 10th commit message:
Fix the fe-text occurrence of fe_add_ban_list().
This is the 11th commit message:
Fixed also fe_ban_list_end() and removed fe_is_banwindow().
This is the 12th commit message:
Use old-style initialization for array of structures modes[]
This is the 13th commit message:
Oops, incomplete regression of modes[] initialization. This fixes.
This is the 14th commit message:
Fixed strptime buy implementing a special version here.
Fixed column width concerns by setting resizable and autosize.
This is the 15th commit message:
Get rid of testing line.
This is the 16th commit message:
Changed to gtkutil_destroy_on_esc ()
This is the 17th commit message:
Remove no-longer-used functnion
This is the 18th commit message:
Minor cleanups to banlist.c, banlist.h
Please enter the commit message for your changes. Lines starting
with '#' will be ignored, and an empty message aborts the commit.
Author: RichardHitt <rbh00@netcom.com>
Committer: Richard Hitt <rbh00@f17.rbh00.pacbell.net>
Not currently on any branch.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: src/common/fe.h
modified: src/common/hexchat.h
modified: src/common/inbound.c
modified: src/common/modes.c
modified: src/common/proto-irc.c
modified: src/common/server.c
modified: src/fe-gtk/banlist.c
modified: src/fe-gtk/banlist.h
modified: src/fe-gtk/fe-gtk.c
modified: src/fe-gtk/fe-gtk.h
modified: src/fe-gtk/maingui.c
modified: src/fe-text/fe-text.c
2013-03-05 03:13:51 -05:00
|
|
|
} else if (strcmp (word[w], "INVEX") == 0)
|
|
|
|
{
|
|
|
|
/* supports mode letter +I, default channel invite */
|
|
|
|
serv->have_invite = TRUE;
|
2011-02-23 22:14:30 -05:00
|
|
|
} else if (strncmp (word[w], "ELIST=", 6) == 0)
|
|
|
|
{
|
|
|
|
/* supports LIST >< min/max user counts? */
|
|
|
|
if (strchr (word[w] + 6, 'U') || strchr (word[w] + 6, 'u'))
|
|
|
|
serv->use_listargs = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
w++;
|
|
|
|
}
|
|
|
|
}
|