1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-30 06:46:38 +02:00

irc: update channel modes by using chanmodes from message 005 (do not send extra command "MODES" to server), fix parsing of modes (bug #36215)

This commit is contained in:
Sebastien Helleu
2012-05-15 12:48:50 +02:00
parent 36d5f464fb
commit 21b87c428c
9 changed files with 351 additions and 174 deletions
+3 -1
View File
@@ -1,7 +1,7 @@
WeeChat ChangeLog
=================
Sébastien Helleu <flashcode@flashtux.org>
v0.3.8-dev, 2012-05-11
v0.3.8-dev, 2012-05-15
Version 0.3.8 (under dev!)
@@ -33,6 +33,8 @@ Version 0.3.8 (under dev!)
* scripts: fix type of argument "rc" in callback of hook_process (from string to
integer)
* guile: add missing function "hook_process_hashtable" in API
* irc: update channel modes by using chanmodes from message 005 (do not send
extra command "MODES" to server), fix parsing of modes (bug #36215)
* irc: add option "fakerecv" for command /server to simulate a received IRC
message (not documented, for debug only)
* irc: hide everything after "identify" or "register" in messages to nickserv
+295 -66
View File
@@ -22,6 +22,7 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../weechat-plugin.h"
@@ -33,25 +34,246 @@
/*
* irc_mode_channel_set: set channel modes
* return: 1 if channel modes are updated
* 0 if channel modes are NOT updated
* (no update or on nicks only)
* irc_mode_get_chanmode_type: get type of channel mode, which is a letter from
* 'A' to 'D':
* A = Mode that adds or removes a nick or address to a list. Always has a parameter.
* B = Mode that changes a setting and always has a parameter.
* C = Mode that changes a setting and only has a parameter when set.
* D = Mode that changes a setting and never has a parameter.
* Example:
* CHANMODES=beI,k,l,imnpstaqr
* A = { b, e, I }
* B = { k }
* C = { l }
* D = { i, m, n, p, s, t, a, q, r }
* Note: Modes of type A return the list when there is no parameter present.
* Note: Some clients assumes that any mode not listed is of type D.
* Note: Modes in PREFIX are not listed but could be considered type B.
* More info: http://www.irc.org/tech_docs/005.html
*/
int
irc_mode_channel_set (struct t_irc_server *server,
struct t_irc_channel *channel, const char *modes)
char
irc_mode_get_chanmode_type (struct t_irc_server *server, char chanmode)
{
char *pos_args, *str_modes, set_flag, **argv, *pos, *ptr_arg;
int modes_count, channel_modes_updated, argc, current_arg;
char chanmode_type, *pos;
const char *chanmodes, *ptr_chanmodes;
chanmodes = irc_server_get_chanmodes (server);
pos = strchr (chanmodes, chanmode);
if (pos)
{
chanmode_type = 'A';
for (ptr_chanmodes = chanmodes; ptr_chanmodes < pos; ptr_chanmodes++)
{
if (ptr_chanmodes[0] == ',')
{
if (chanmode_type == 'D')
break;
chanmode_type++;
}
}
return chanmode_type;
}
/* assume it is type 'B' if mode is not in chanmodes but in prefix */
if (irc_server_get_prefix_mode_index (server, chanmode) >= 0)
return 'B';
/* unknown mode, type 'D' by default */
return 'D';
}
/*
* irc_mode_channel_update: update channel modes using the mode and argument
* Example:
* if channel modes are "+tn" and that we have:
* - set_flag = '+'
* - chanmode = 'k'
* - chanmode_type = 'B'
* - argument = 'password'
* then channel modes become:
* "+tnk password"
*/
void
irc_mode_channel_update (struct t_irc_server *server,
struct t_irc_channel *channel,
char set_flag,
char chanmode,
const char *argument)
{
char *pos_args, *str_modes, **argv, *pos, *ptr_arg;
char *new_modes, *new_args, str_mode[2], *str_temp;
int argc, current_arg, chanmode_found, length;
if (!channel->modes)
channel->modes = strdup ("+");
if (!channel->modes)
return;
argc = 0;
argv = NULL;
pos_args = strchr (channel->modes, ' ');
if (pos_args)
{
str_modes = weechat_strndup (channel->modes, pos_args - channel->modes);
if (!str_modes)
return;
pos_args++;
while (pos_args[0] == ' ')
pos_args++;
argv = weechat_string_split (pos_args, " ", 0, 0, &argc);
}
else
{
str_modes = strdup (channel->modes);
if (!str_modes)
return;
}
new_modes = malloc (strlen (channel->modes) + 1 + 1);
new_args = malloc (((pos_args) ? strlen (pos_args) : 0)
+ ((argument) ? 1 + strlen (argument) : 0) + 1);
if (new_modes && new_args)
{
new_modes[0] = '\0';
new_args[0] = '\0';
/* loop on current modes and build "new_modes" + "new_args" */
current_arg = 0;
chanmode_found = 0;
pos = str_modes;
while (pos && pos[0])
{
if ((pos[0] == '+') || (pos[0] == '-'))
{
str_mode[0] = pos[0];
str_mode[1] = '\0';
strcat (new_modes, str_mode);
}
else
{
ptr_arg = NULL;
switch (irc_mode_get_chanmode_type (server, pos[0]))
{
case 'A': /* always argument */
case 'B': /* always argument */
case 'C': /* argumment if set */
ptr_arg = (current_arg < argc) ?
argv[current_arg] : NULL;
break;
case 'D': /* no argument */
break;
}
if (ptr_arg)
current_arg++;
if (pos[0] == chanmode)
{
chanmode_found = 1;
if (set_flag == '+')
{
str_mode[0] = pos[0];
str_mode[1] = '\0';
strcat (new_modes, str_mode);
if (argument)
{
if (new_args[0])
strcat (new_args, " ");
strcat (new_args, argument);
}
}
}
else
{
str_mode[0] = pos[0];
str_mode[1] = '\0';
strcat (new_modes, str_mode);
if (ptr_arg)
{
if (new_args[0])
strcat (new_args, " ");
strcat (new_args, ptr_arg);
}
}
}
pos++;
}
if (!chanmode_found)
{
/*
* chanmode was not in channel modes: if set_flag is '+', add
* it to channel modes
*/
if (set_flag == '+')
{
if (argument)
{
/* add mode with argument at the end of modes */
str_mode[0] = chanmode;
str_mode[1] = '\0';
strcat (new_modes, str_mode);
if (new_args[0])
strcat (new_args, " ");
strcat (new_args, argument);
}
else
{
/* add mode without argument at the beginning of modes */
pos = new_modes;
while (pos[0] == '+')
pos++;
memmove (pos + 1, pos, strlen (pos) + 1);
pos[0] = chanmode;
}
}
}
if (new_args[0])
{
length = strlen (new_modes) + 1 + strlen (new_args) + 1;
str_temp = malloc (length);
if (str_temp)
{
snprintf (str_temp, length, "%s %s", new_modes, new_args);
if (channel->modes)
free (channel->modes);
channel->modes = strdup (str_temp);
}
}
else
{
if (channel->modes)
free (channel->modes);
channel->modes = strdup (new_modes);
}
free (new_modes);
free (new_args);
}
if (str_modes)
free (str_modes);
if (argv)
weechat_string_free_split (argv);
}
/*
* irc_mode_channel_set: set channel modes using CHANMODES (from message 005)
* and update channel modes if needed
*/
void
irc_mode_channel_set (struct t_irc_server *server,
struct t_irc_channel *channel,
const char *modes)
{
char *pos_args, *str_modes, set_flag, **argv, *pos, *ptr_arg, chanmode_type;
int argc, current_arg, update_channel_modes, channel_modes_updated;
struct t_irc_nick *ptr_nick;
if (!server || !channel || !modes)
return 0;
return;
channel_modes_updated = 0;
argc = 0;
argv = NULL;
pos_args = strchr (modes, ' ');
@@ -59,7 +281,7 @@ irc_mode_channel_set (struct t_irc_server *server,
{
str_modes = weechat_strndup (modes, pos_args - modes);
if (!str_modes)
return 0;
return;
pos_args++;
while (pos_args[0] == ' ')
pos_args++;
@@ -69,22 +291,10 @@ irc_mode_channel_set (struct t_irc_server *server,
{
str_modes = strdup (modes);
if (!str_modes)
return 0;
return;
}
/* count number of mode chars */
modes_count = 0;
pos = str_modes;
while (pos && pos[0])
{
if ((pos[0] != ':') && (pos[0] != ' ') && (pos[0] != '+')
&& (pos[0] != '-'))
{
modes_count++;
}
pos++;
}
current_arg = argc - modes_count;
current_arg = 0;
if (str_modes && str_modes[0])
{
@@ -103,43 +313,60 @@ irc_mode_channel_set (struct t_irc_server *server,
case '-':
set_flag = '-';
break;
case 'b': /* ban (ignored) */
current_arg++;
break;
case 'k': /* channel key */
if (channel->key)
{
free (channel->key);
channel->key = NULL;
}
if (set_flag == '+')
{
ptr_arg = ((current_arg >= 0) && (current_arg < argc)) ?
argv[current_arg] : NULL;
if (ptr_arg)
channel->key = strdup (ptr_arg);
}
channel_modes_updated = 1;
current_arg++;
break;
case 'l': /* channel limit */
if (set_flag == '-')
channel->limit = 0;
if (set_flag == '+')
{
ptr_arg = ((current_arg >= 0) && (current_arg < argc)) ?
argv[current_arg] : NULL;
if (ptr_arg)
channel->limit = atoi (ptr_arg);
}
channel_modes_updated = 1;
current_arg++;
break;
default:
if (irc_server_get_prefix_mode_index (server, pos[0]) >= 0)
update_channel_modes = 1;
chanmode_type = irc_mode_get_chanmode_type (server, pos[0]);
ptr_arg = NULL;
switch (chanmode_type)
{
ptr_arg = ((current_arg >= 0) && (current_arg < argc)) ?
argv[current_arg] : NULL;
case 'A': /* always argument */
update_channel_modes = 0;
ptr_arg = (current_arg < argc) ?
argv[current_arg] : NULL;
break;
case 'B': /* always argument */
ptr_arg = (current_arg < argc) ?
argv[current_arg] : NULL;
break;
case 'C': /* argumment if set */
ptr_arg = ((set_flag == '+') && (current_arg < argc)) ?
argv[current_arg] : NULL;
break;
case 'D': /* no argument */
break;
}
if (ptr_arg)
current_arg++;
if (pos[0] == 'k')
{
/* channel key */
if (channel->key)
{
free (channel->key);
channel->key = NULL;
}
if ((set_flag == '+') && ptr_arg)
{
channel->key = strdup (ptr_arg);
}
}
else if (pos[0] == 'l')
{
/* channel limit */
if (set_flag == '-')
channel->limit = 0;
if ((set_flag == '+') && ptr_arg)
{
channel->limit = atoi (ptr_arg);
}
}
else if ((chanmode_type != 'A')
&& (irc_server_get_prefix_mode_index (server,
pos[0]) >= 0))
{
/* mode for nick */
update_channel_modes = 0;
if (ptr_arg)
{
ptr_nick = irc_nick_search (server, channel,
@@ -151,9 +378,12 @@ irc_mode_channel_set (struct t_irc_server *server,
}
}
}
else
if (update_channel_modes)
{
irc_mode_channel_update (server, channel, set_flag,
pos[0], ptr_arg);
channel_modes_updated = 1;
current_arg++;
}
break;
}
pos++;
@@ -165,9 +395,8 @@ irc_mode_channel_set (struct t_irc_server *server,
if (argv)
weechat_string_free_split (argv);
weechat_bar_item_update ("buffer_name");
return channel_modes_updated;
if (channel_modes_updated)
weechat_bar_item_update ("buffer_name");
}
/*
+3 -3
View File
@@ -23,9 +23,9 @@
struct t_irc_server;
struct t_irc_channel;
extern int irc_mode_channel_set (struct t_irc_server *server,
struct t_irc_channel *channel,
const char *modes);
extern void irc_mode_channel_set (struct t_irc_server *server,
struct t_irc_channel *channel,
const char *modes);
extern void irc_mode_user_set (struct t_irc_server *server, const char *modes,
int reset_modes);
+16 -97
View File
@@ -816,7 +816,6 @@ IRC_PROTOCOL_CALLBACK(mode)
struct t_irc_channel *ptr_channel;
struct t_irc_nick *ptr_nick;
struct t_gui_buffer *ptr_buffer;
struct t_hashtable *hashtable;
/*
* MODE message looks like:
@@ -832,39 +831,7 @@ IRC_PROTOCOL_CALLBACK(mode)
{
ptr_channel = irc_channel_search (server, argv[2]);
if (ptr_channel)
{
if (ptr_channel->modes)
{
if (irc_mode_channel_set (server, ptr_channel, pos_modes))
{
hashtable = weechat_hashtable_new (8,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL,
NULL);
if (hashtable)
{
weechat_hashtable_set (hashtable, "server", server->name);
weechat_hashtable_set (hashtable, "pattern", "mode_channel");
weechat_hashtable_set (hashtable, "signal", "mode");
weechat_hashtable_set (hashtable, "string", ptr_channel->name);
weechat_hook_hsignal_send ("irc_redirect_command", hashtable);
irc_server_sendf (server, IRC_SERVER_SEND_OUTQ_PRIO_LOW,
NULL, "MODE %s", ptr_channel->name);
weechat_hashtable_free (hashtable);
}
else
{
irc_server_sendf (server, IRC_SERVER_SEND_OUTQ_PRIO_LOW,
NULL, "MODE %s", ptr_channel->name);
}
}
}
else
{
(void) irc_mode_channel_set (server, ptr_channel, pos_modes);
}
}
irc_mode_channel_set (server, ptr_channel, pos_modes);
ptr_nick = irc_nick_search (server, ptr_channel, nick);
ptr_buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer;
weechat_printf_tags (irc_msgbuffer_get_target_buffer (server, NULL,
@@ -2169,6 +2136,21 @@ IRC_PROTOCOL_CALLBACK(005)
pos2[0] = ' ';
}
/* save chanmodes */
pos = strstr (argv_eol[3], "CHANMODES=");
if (pos)
{
pos += 10;
pos2 = strchr (pos, ' ');
if (pos2)
pos2[0] = '\0';
if (server->chanmodes)
free (server->chanmodes);
server->chanmodes = strdup (pos);
if (pos2)
pos2[0] = ' ';
}
/* save whole message (concatenate to existing isupport, if any) */
pos_start = NULL;
pos = strstr (argv_eol[3], " :");
@@ -4409,69 +4391,6 @@ IRC_PROTOCOL_CALLBACK(sasl_end)
return WEECHAT_RC_OK;
}
/*
* irc_protocol_redirection_mode_cb: callback for redirection of "mode" command
*/
int
irc_protocol_redirection_mode_cb (void *data, const char *signal,
struct t_hashtable *hashtable)
{
const char *output, *server;
char **messages, *command, *arguments, **argv, **argv_eol;
int num_messages, argc, i;
struct t_irc_server *ptr_server;
struct t_irc_channel *ptr_channel;
/* make C compiler happy */
(void) data;
(void) signal;
output = weechat_hashtable_get (hashtable, "output");
server = weechat_hashtable_get (hashtable, "server");
if (!output || !server)
return WEECHAT_RC_OK;
ptr_server = irc_server_search (server);
if (!ptr_server)
return WEECHAT_RC_OK;
messages = weechat_string_split (output, "\n", 0, 0, &num_messages);
if (messages)
{
for (i = 0; i < num_messages; i++)
{
irc_message_parse (ptr_server, messages[i], NULL, NULL,
&command, NULL, &arguments);
if (command && (strcmp (command, "324") == 0) && arguments)
{
argv = weechat_string_split (arguments, " ", 0, 0, &argc);
argv_eol = weechat_string_split (arguments, " ", 1, 0, NULL);
if (argv && argv_eol && (argc >= 2))
{
ptr_channel = irc_channel_search (ptr_server, argv[1]);
if (ptr_channel)
{
irc_channel_set_modes (ptr_channel,
(argc >= 3) ? argv_eol[2] : NULL);
if (argc >= 3)
{
irc_mode_channel_set (ptr_server, ptr_channel,
ptr_channel->modes);
}
}
}
if (argv)
weechat_string_free_split (argv);
if (argv_eol)
weechat_string_free_split (argv_eol);
}
}
weechat_string_free_split (messages);
}
return WEECHAT_RC_OK;
}
/*
* irc_protocol_recv_command: executes action when receiving IRC command
* return: 0 = all ok, command executed
-2
View File
@@ -79,8 +79,6 @@ struct t_irc_protocol_msg
extern const char *irc_protocol_tags (const char *command, const char *tags,
const char *nick);
extern int irc_protocol_redirection_mode_cb (void *data, const char *signal,
struct t_hashtable *hashtable);
extern void irc_protocol_recv_command (struct t_irc_server *server,
const char *irc_message,
const char *msg_command,
+21 -1
View File
@@ -100,6 +100,7 @@ char *irc_server_casemapping_string[IRC_SERVER_NUM_CASEMAPPING] =
char *irc_server_prefix_modes_default = "qaohvu";
char *irc_server_prefix_chars_default = "~&@%+-";
char *irc_server_chanmodes_default = "beI,k,l";
const char *irc_server_send_default_tags = NULL; /* default tags when */
/* sending a message */
@@ -486,7 +487,7 @@ irc_server_get_nick_index (struct t_irc_server *server)
/*
* irc_server_get_isupport_value: return value of an item in "isupport" (copy
* of IRC message 005)
* if featureis found but has no value, empty
* if feature is found but has no value, empty
* string is returned
* if feature is not found, NULL is returned
*/
@@ -710,6 +711,18 @@ irc_server_get_prefix_char_for_mode (struct t_irc_server *server, char mode)
return ' ';
}
/*
* irc_server_get_chanmodes: get chanmodes for server (return default chanmodes
* if chanmodes is not set)
*/
const char *
irc_server_get_chanmodes (struct t_irc_server *server)
{
return (server && server->chanmodes) ?
server->chanmodes : irc_server_chanmodes_default;
}
/*
* irc_server_alloc: allocate a new server and add it to the servers queue
*/
@@ -776,6 +789,7 @@ irc_server_alloc (const char *name)
new_server->nick_max_length = 0;
new_server->casemapping = IRC_SERVER_CASEMAPPING_RFC1459;
new_server->chantypes = NULL;
new_server->chanmodes = NULL;
new_server->reconnect_delay = 0;
new_server->reconnect_start = 0;
new_server->command_time = 0;
@@ -1237,6 +1251,8 @@ irc_server_free_data (struct t_irc_server *server)
free (server->prefix_chars);
if (server->chantypes)
free (server->chantypes);
if (server->chanmodes)
free (server->chanmodes);
if (server->away_message)
free (server->away_message);
if (server->cmd_list_regexp)
@@ -4272,6 +4288,7 @@ irc_server_hdata_server_cb (void *data, const char *hdata_name)
WEECHAT_HDATA_VAR(struct t_irc_server, nick_max_length, INTEGER, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, casemapping, INTEGER, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, chantypes, STRING, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, chanmodes, STRING, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reconnect_delay, INTEGER, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reconnect_start, TIME, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, command_time, TIME, NULL);
@@ -4468,6 +4485,8 @@ irc_server_add_to_infolist (struct t_infolist *infolist,
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "chantypes", server->chantypes))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "chanmodes", server->chanmodes))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "reconnect_delay", server->reconnect_delay))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "reconnect_start", server->reconnect_start))
@@ -4781,6 +4800,7 @@ irc_server_print_log ()
ptr_server->casemapping,
irc_server_casemapping_string[ptr_server->casemapping]);
weechat_log_printf (" chantypes. . . . . . : '%s'", ptr_server->chantypes);
weechat_log_printf (" chanmodes. . . . . . : '%s'", ptr_server->chanmodes);
weechat_log_printf (" reconnect_delay. . . : %d", ptr_server->reconnect_delay);
weechat_log_printf (" reconnect_start. . . : %ld", ptr_server->reconnect_start);
weechat_log_printf (" command_time . . . . : %ld", ptr_server->command_time);
+3
View File
@@ -169,6 +169,8 @@ struct t_irc_server
int nick_max_length; /* max lenth of nick (from msg 005) */
int casemapping; /* casemapping from msg 005 */
char *chantypes; /* chantypes from msg 005 (eg "&#") */
char *chanmodes; /* chanmodes from msg 005 */
/* (eg "beI,k,l,imnpstaqr") */
int reconnect_delay; /* current reconnect delay (growing) */
time_t reconnect_start; /* this time + delay = reconnect time */
time_t command_time; /* this time + command_delay = time to */
@@ -250,6 +252,7 @@ extern char irc_server_get_prefix_mode_for_char (struct t_irc_server *server,
char prefix_char);
extern char irc_server_get_prefix_char_for_mode (struct t_irc_server *server,
char mode);
extern const char *irc_server_get_chanmodes (struct t_irc_server *server);
extern struct t_irc_server *irc_server_alloc (const char *name);
extern struct t_irc_server *irc_server_alloc_with_url (const char *irc_url);
extern void irc_server_apply_command_line_options (struct t_irc_server *server,
+10
View File
@@ -377,6 +377,16 @@ irc_upgrade_read_cb (void *data,
str = weechat_infolist_string (infolist, "chantypes");
if (str)
irc_upgrade_current_server->chantypes = strdup (str);
str = weechat_infolist_string (infolist, "chanmodes");
if (str)
irc_upgrade_current_server->chanmodes = strdup (str);
else
{
str = irc_server_get_isupport_value (irc_upgrade_current_server,
"CHANMODES");
if (str)
irc_upgrade_current_server->chanmodes = strdup (str);
}
irc_upgrade_current_server->reconnect_delay = weechat_infolist_integer (infolist, "reconnect_delay");
irc_upgrade_current_server->reconnect_start = weechat_infolist_time (infolist, "reconnect_start");
irc_upgrade_current_server->command_time = weechat_infolist_time (infolist, "command_time");
-4
View File
@@ -185,10 +185,6 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
weechat_hook_hsignal ("irc_redirect_pattern", &irc_redirect_pattern_hsignal_cb, NULL);
weechat_hook_hsignal ("irc_redirect_command", &irc_redirect_command_hsignal_cb, NULL);
/* hook hsignal for redirection of "mode" output */
weechat_hook_hsignal ("irc_redirection_mode_mode_channel",
&irc_protocol_redirection_mode_cb, NULL);
/* modifiers */
weechat_hook_modifier ("irc_color_decode", &irc_color_modifier_cb, NULL);
weechat_hook_modifier ("irc_color_encode", &irc_color_modifier_cb, NULL);