summaryrefslogtreecommitdiff
path: root/src/irc/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/core')
-rw-r--r--src/irc/core/irc-commands.c43
-rw-r--r--src/irc/core/irc-expandos.c11
-rw-r--r--src/irc/core/irc-nicklist.c54
-rw-r--r--src/irc/core/irc-nicklist.h7
-rw-r--r--src/irc/core/irc-queries.c15
-rw-r--r--src/irc/core/irc-queries.h3
-rw-r--r--src/irc/core/irc-servers.c147
-rw-r--r--src/irc/core/irc-servers.h8
-rw-r--r--src/irc/core/irc-session.c41
-rw-r--r--src/irc/core/irc.h5
-rw-r--r--src/irc/core/modes.c192
-rw-r--r--src/irc/core/modes.h41
-rw-r--r--src/irc/core/netsplit.c1
-rw-r--r--src/irc/core/netsplit.h1
14 files changed, 451 insertions, 118 deletions
diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c
index 9ac6ca66..f760d234 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -623,28 +623,39 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
chanrec = irc_channel_find(server, channame);
if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
- /* send notice to all ops */
- nicks = NULL;
- g_hash_table_foreach(chanrec->nicks, (GHFunc) cmd_wall_hash, &nicks);
-
- args = g_strconcat(chanrec->name, " ", msg, NULL);
- msg = parse_special_string(settings_get_str("wall_format"),
- SERVER(server), item, args, NULL, 0);
- g_free(args);
-
- for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
- NICK_REC *rec = tmp->data;
-
- if (rec != chanrec->ownnick)
- irc_send_cmdv(server, "NOTICE %s :%s", rec->nick, msg);
+ /* See if the server has advertised support of wallchops */
+ if (g_hash_table_lookup(chanrec->server->isupport, "statusmsg") ||
+ g_hash_table_lookup(chanrec->server->isupport, "wallchops"))
+ irc_send_cmdv(server, "NOTICE @%s :%s", chanrec->name, msg);
+ else {
+ /* Fall back to manually noticing each op */
+ nicks = NULL;
+ g_hash_table_foreach(chanrec->nicks,
+ (GHFunc) cmd_wall_hash, &nicks);
+
+ args = g_strconcat(chanrec->name, " ", msg, NULL);
+ msg = parse_special_string(settings_get_str("wall_format"),
+ SERVER(server), item, args, NULL, 0);
+ g_free(args);
+
+ for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+ NICK_REC *rec = tmp->data;
+
+ if (rec != chanrec->ownnick) {
+ irc_send_cmdv(server, "NOTICE %s :%s",
+ rec->nick, msg);
+ }
+ }
+ g_free(msg);
+ g_slist_free(nicks);
}
- g_free(msg);
- g_slist_free(nicks);
cmd_params_free(free_arg);
}
/* SYNTAX: WALLCHOPS <channel> <message> */
+/* ircu is the only major server i can see which supports this
+ and it supports NOTICE @#channel anyway */
static void cmd_wallchops(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
char *channame, *msg;
diff --git a/src/irc/core/irc-expandos.c b/src/irc/core/irc-expandos.c
index 6acfed73..589b0620 100644
--- a/src/irc/core/irc-expandos.c
+++ b/src/irc/core/irc-expandos.c
@@ -82,10 +82,19 @@ static char *expando_usermode(SERVER_REC *server, void *item, int *free_ret)
return IS_IRC_SERVER(server) ? IRC_SERVER(server)->usermode : "";
}
-/* expands to your usermode on channel, op '@', halfop '%', "+" voice */
+/* 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 ? "+" : "";
diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c
index eb3aaeca..e5d25d5c 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -74,11 +74,32 @@ char *irc_nick_strip(const char *nick)
return stripped;
}
+int irc_nickcmp_rfc1459(const char *m, const char *n)
+{
+ while (*m != '\0' && *n != '\0') {
+ if (to_rfc1459(*m) != to_rfc1459(*n))
+ return -1;
+ m++; n++;
+ }
+ return *m == *n ? 0 : 1;
+}
+
+int irc_nickcmp_ascii(const char *m, const char *n)
+{
+ while (*m != '\0' && *n != '\0') {
+ if (to_ascii(*m) != to_ascii(*n))
+ return -1;
+ m++; n++;
+ }
+ return *m == *n ? 0 : 1;
+}
+
static void event_names_list(IRC_SERVER_REC *server, const char *data)
{
IRC_CHANNEL_REC *chanrec;
+ NICK_REC *rec;
char *params, *type, *channel, *names, *ptr;
- int op, halfop, voice;
+ int op, halfop, voice, other;
g_return_if_fail(data != NULL);
@@ -117,8 +138,8 @@ static void event_names_list(IRC_SERVER_REC *server, const char *data)
showing "@+nick" and since none of these chars are valid
nick chars, just check them until a non-nickflag char is
found. FIXME: we just ignore owner char now. */
- op = halfop = voice = FALSE;
- while (isnickflag(*ptr)) {
+ op = halfop = voice = other = FALSE;
+ while (isnickflag(server, *ptr)) {
switch (*ptr) {
case '@':
op = TRUE;
@@ -129,13 +150,17 @@ static void event_names_list(IRC_SERVER_REC *server, const char *data)
case '+':
voice = TRUE;
break;
+ default:
+ other = *ptr;
}
ptr++;
}
if (nicklist_find((CHANNEL_REC *) chanrec, ptr) == NULL) {
- irc_nicklist_insert(chanrec, ptr, op, halfop,
- voice, FALSE);
+ rec = irc_nicklist_insert(chanrec, ptr, op, halfop,
+ voice, FALSE);
+ if (other)
+ rec->other = other;
}
}
@@ -376,17 +401,24 @@ static void sig_usermode(SERVER_REC *server)
nicklist_update_flags(server, server->nick, server->usermode_away, -1);
}
-static const char *get_nick_flags(void)
+static const char *get_nick_flags(SERVER_REC *server)
{
- return "@+%";
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+ static char *std = "@%+";
+ char *prefix = g_hash_table_lookup(irc_server->isupport, "prefix");
+
+ if (prefix == NULL)
+ return std;
+ prefix = strchr(prefix, ')');
+ if (prefix != NULL || *++prefix == '\0') /* FIXME: ugly to modify it */
+ return std;
+ return prefix;
}
static void sig_connected(IRC_SERVER_REC *server)
{
- if (IS_IRC_SERVER(server)) {
- server->get_nick_flags =
- (const char *(*)(void)) get_nick_flags;
- }
+ if (IS_IRC_SERVER(server))
+ server->get_nick_flags = get_nick_flags;
}
void irc_nicklist_init(void)
diff --git a/src/irc/core/irc-nicklist.h b/src/irc/core/irc-nicklist.h
index 0b392c77..f2813ea1 100644
--- a/src/irc/core/irc-nicklist.h
+++ b/src/irc/core/irc-nicklist.h
@@ -10,7 +10,14 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
char *irc_nick_strip(const char *nick);
+int irc_nickcmp_rfc1459(const char *, const char *);
+int irc_nickcmp_ascii(const char *, const char *);
+
void irc_nicklist_init(void);
void irc_nicklist_deinit(void);
+/* FIXME: to_rfc1459() is missing things */
+#define to_rfc1459(x) ((x) >= 65 && (x) <= 94 ? (x) + 32 : (x))
+#define to_ascii(x) ((x) >= 65 && (x) <= 94 ? (x) + 32 : (x))
+
#endif
diff --git a/src/irc/core/irc-queries.c b/src/irc/core/irc-queries.c
index 0ca1b94e..a2a545f5 100644
--- a/src/irc/core/irc-queries.c
+++ b/src/irc/core/irc-queries.c
@@ -22,6 +22,7 @@
#include "signals.h"
#include "misc.h"
+#include "irc-nicklist.h"
#include "irc-servers.h"
#include "irc-queries.h"
@@ -40,6 +41,20 @@ QUERY_REC *irc_query_create(const char *server_tag,
return rec;
}
+QUERY_REC *irc_query_find(IRC_SERVER_REC *server, const char *nick)
+{
+ GSList *tmp;
+
+ for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+ QUERY_REC *rec = tmp->data;
+
+ if (server->nick_comp_func(rec->name, nick) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
static void check_query_changes(IRC_SERVER_REC *server, const char *nick,
const char *address, const char *target)
{
diff --git a/src/irc/core/irc-queries.h b/src/irc/core/irc-queries.h
index e95602c1..0a3cc7a5 100644
--- a/src/irc/core/irc-queries.h
+++ b/src/irc/core/irc-queries.h
@@ -14,8 +14,7 @@
void irc_queries_init(void);
void irc_queries_deinit(void);
-#define irc_query_find(server, name) \
- query_find(SERVER(server), name)
+QUERY_REC *irc_query_find(IRC_SERVER_REC *server, const char *nick);
QUERY_REC *irc_query_create(const char *server_tag,
const char *nick, int automatic);
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index c6d16b62..6e139c9b 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -30,6 +30,8 @@
#include "channels.h"
#include "queries.h"
+#include "irc-nicklist.h"
+#include "irc-queries.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
#include "channel-rejoin.h"
@@ -55,9 +57,11 @@ void irc_servers_reconnect_deinit(void);
static int cmd_tag;
-static int isnickflag_func(char flag)
+static int isnickflag_func(SERVER_REC *server, char flag)
{
- return isnickflag(flag);
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+
+ return isnickflag(irc_server, flag);
}
static int ischannel_func(SERVER_REC *server, const char *data)
@@ -157,6 +161,9 @@ static void server_init(IRC_SERVER_REC *server)
conn->address, conn->port);
}
+ server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
+ (GCompareFunc) g_istr_equal);
+
server->cmdcount = 0;
}
@@ -198,6 +205,8 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
ircconn->max_msgs : DEFAULT_MAX_MSGS;
server->connrec->use_ssl = conn->use_ssl;
+ modes_server_init(server);
+
server_connect_init((SERVER_REC *) server);
return (SERVER_REC *) server;
}
@@ -268,6 +277,9 @@ static void sig_connected(IRC_SERVER_REC *server)
server->isnickflag = isnickflag_func;
server->ischannel = ischannel_func;
server->send_message = send_message;
+ server->query_find_func =
+ (QUERY_REC *(*)(SERVER_REC *, const char *)) irc_query_find;
+ server->nick_comp_func = irc_nickcmp_ascii;
server->splits = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);
@@ -276,6 +288,12 @@ static void sig_connected(IRC_SERVER_REC *server)
server_init(server);
}
+static void isupport_destroy_hash(void *key, void *value)
+{
+ g_free(key);
+ g_free(value);
+}
+
static void sig_disconnected(IRC_SERVER_REC *server)
{
GSList *tmp;
@@ -291,6 +309,10 @@ static void sig_disconnected(IRC_SERVER_REC *server)
g_slist_free(server->cmdqueue);
server->cmdqueue = NULL;
+ g_hash_table_foreach(server->isupport, (GHFunc) isupport_destroy_hash, server);
+ g_hash_table_destroy(server->isupport);
+ server->isupport = NULL;
+
g_free_and_null(server->wanted_usermode);
g_free_and_null(server->real_address);
g_free_and_null(server->usermode);
@@ -452,7 +474,7 @@ static int sig_set_user_mode(IRC_SERVER_REC *server)
mode = server->connrec->usermode;
newmode = server->usermode == NULL ? NULL :
- modes_join(server->usermode, mode, FALSE);
+ modes_join(NULL, server->usermode, mode, FALSE);
if (newmode == NULL || strcmp(newmode, server->usermode) != 0) {
/* change the user mode. we used to do some trickery to
@@ -521,6 +543,123 @@ static void event_server_info(IRC_SERVER_REC *server, const char *data)
g_free(params);
}
+static void parse_chanmodes(IRC_SERVER_REC *server, const char *sptr)
+{
+ mode_func_t *modefuncs[] = {
+ modes_type_a,
+ modes_type_b,
+ modes_type_c,
+ modes_type_d
+ };
+ char **item, **chanmodes;
+ int i;
+
+ chanmodes = g_strsplit(sptr, ",", 5); /* ignore extras */
+
+ for (i = 0, item = chanmodes; *item != NULL && i < 4; item++, i++) {
+ unsigned char *p = *item;
+ while (*p != '\0') {
+ server->modes[(int)*p].func = modefuncs[i];
+ p++;
+ }
+ }
+
+ g_strfreev(chanmodes);
+}
+
+static void parse_prefix(IRC_SERVER_REC *server, const char *sptr)
+{
+ const char *eptr;
+
+ if (*sptr++ != '(')
+ return; /* Unknown prefix format */
+
+ eptr = strchr(sptr, ')');
+ if (eptr == NULL)
+ return;
+
+ eptr++;
+ while (*sptr != '\0' && *eptr != '\0' && *sptr != ')' && *eptr != ' ') {
+ server->modes[(int)(unsigned char) *sptr].func =
+ modes_type_prefix;
+ server->modes[(int)(unsigned char) *sptr].prefix = *eptr;
+ server->prefix[(int)(unsigned char) *eptr] = *sptr;
+ sptr++; eptr++;
+ }
+}
+
+static void event_isupport(IRC_SERVER_REC *server, const char *data)
+{
+ char **item, *sptr, *eptr;
+ char **isupport;
+
+ g_return_if_fail(server != NULL);
+
+ server->isupport_sent = TRUE;
+
+ sptr = strchr(data, ' ');
+ if (sptr == NULL)
+ return;
+ sptr++;
+
+ isupport = g_strsplit(sptr, " ", -1);
+
+ for(item = isupport; *item != NULL; item++) {
+ int removed = FALSE;
+ gpointer key = NULL, value = NULL;
+
+ if (**item == ':')
+ break;
+
+ sptr = strchr(*item, '=');
+ if (sptr != NULL) {
+ *sptr = '\0';
+ sptr++;
+ }
+
+ eptr = *item;
+ if(*eptr == '-') {
+ removed = TRUE;
+ eptr++;
+ }
+
+ if (!g_hash_table_lookup_extended(server->isupport, eptr,
+ &key, &value) && removed)
+ continue;
+
+ if (removed)
+ g_hash_table_remove(server->isupport, eptr);
+ else {
+ g_hash_table_insert(server->isupport, g_strdup(eptr),
+ g_strdup(sptr != NULL ? sptr : ""));
+ }
+
+ g_free(key);
+ g_free(value);
+ }
+ g_strfreev(isupport);
+
+ if ((sptr = g_hash_table_lookup(server->isupport, "CHANMODES")))
+ parse_chanmodes(server, sptr);
+
+ /* This is after chanmode because some servers define modes in both */
+ if ((sptr = g_hash_table_lookup(server->isupport, "PREFIX")))
+ parse_prefix(server, sptr);
+
+ if ((sptr = g_hash_table_lookup(server->isupport, "MODES"))) {
+ server->max_modes_in_cmd = atoi(sptr);
+ if (server->max_modes_in_cmd < 1)
+ server->max_modes_in_cmd = DEFAULT_MAX_MODES;
+ }
+
+ if ((sptr = g_hash_table_lookup(server->isupport, "CASEMAPPING"))) {
+ if (strstr(sptr, "rfc1459") != NULL)
+ server->nick_comp_func = irc_nickcmp_rfc1459;
+ else
+ server->nick_comp_func = irc_nickcmp_ascii;
+ }
+}
+
static void event_motd(IRC_SERVER_REC *server, const char *data, const char *from)
{
if (server->connected)
@@ -599,6 +738,7 @@ void irc_servers_init(void)
signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit);
signal_add("event 001", (SIGNAL_FUNC) event_connected);
signal_add("event 004", (SIGNAL_FUNC) event_server_info);
+ signal_add("event 005", (SIGNAL_FUNC) event_isupport);
signal_add("event 375", (SIGNAL_FUNC) event_motd);
signal_add_last("event 376", (SIGNAL_FUNC) event_end_of_motd);
signal_add_last("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
@@ -623,6 +763,7 @@ void irc_servers_deinit(void)
signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
signal_remove("event 001", (SIGNAL_FUNC) event_connected);
signal_remove("event 004", (SIGNAL_FUNC) event_server_info);
+ signal_remove("event 005", (SIGNAL_FUNC) event_isupport);
signal_remove("event 375", (SIGNAL_FUNC) event_motd);
signal_remove("event 376", (SIGNAL_FUNC) event_end_of_motd);
signal_remove("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 1286a15e..c1d1d9f0 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -3,6 +3,7 @@
#include "chat-protocols.h"
#include "servers.h"
+#include "modes.h"
/* returns IRC_SERVER_REC if it's IRC server, NULL if it isn't */
#define IRC_SERVER(server) \
@@ -61,6 +62,7 @@ struct _IRC_SERVER_REC {
unsigned int disable_lag:1; /* Disable lag detection (PING command doesn't exist) */
unsigned int nick_collision:1; /* We're just now being killed because of nick collision */
unsigned int motd_got:1; /* We've received MOTD */
+ unsigned int isupport_sent:1; /* Server has sent us an isupport reply */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */
@@ -96,6 +98,12 @@ struct _IRC_SERVER_REC {
channels go here if they're "temporarily unavailable"
because of netsplits */
void *chanqueries;
+
+ GHashTable *isupport;
+ struct modes_type modes[256]; /* Stores the modes sent by a server in an isupport reply */
+ char prefix[256];
+
+ int (*nick_comp_func)(const char *, const char *); /* Function for comparing nicknames on this server */
};
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn);
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
index 3992171d..797e084e 100644
--- a/src/irc/core/irc-session.c
+++ b/src/irc/core/irc-session.c
@@ -22,15 +22,25 @@
#include "signals.h"
#include "net-sendbuffer.h"
#include "lib-config/iconfig.h"
+#include "misc.h"
#include "irc-servers.h"
#include "irc-channels.h"
#include "irc-nicklist.h"
+struct _isupport_data { CONFIG_REC *config; CONFIG_NODE *node; };
+
+static void session_isupport_foreach(char *key, char *value, struct _isupport_data *data)
+{
+ config_node_set_str(data->config, data->node, key, value);
+}
+
static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
CONFIG_NODE *node)
{
GSList *tmp;
+ CONFIG_NODE *isupport;
+ struct _isupport_data isupport_data;
if (!IS_IRC_SERVER(server))
return;
@@ -54,11 +64,19 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_bool(config, node, "usermode_away", server->usermode_away);
config_node_set_str(config, node, "away_reason", server->away_reason);
config_node_set_bool(config, node, "emode_known", server->emode_known);
+
+ isupport = config_node_section(node, "isupport", NODE_TYPE_BLOCK);
+ isupport_data.config = config;
+ isupport_data.node = isupport;
+
+ g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data);
}
static void sig_session_restore_server(IRC_SERVER_REC *server,
CONFIG_NODE *node)
{
+ GSList *tmp;
+
if (!IS_IRC_SERVER(server))
return;
@@ -69,12 +87,33 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->usermode_away = config_node_get_bool(node, "usermode_away", FALSE);
server->away_reason = g_strdup(config_node_get_str(node, "away_reason", NULL));
server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
+
+ if (server->isupport == NULL) {
+ server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
+ (GCompareFunc) g_istr_equal);
+ }
+
+ node = config_node_section(node, "isupport", -1);
+ tmp = config_node_first(node->value);
+ if(tmp != NULL)
+ server->isupport_sent = TRUE;
+
+ for (; tmp != NULL; tmp = config_node_next(tmp)) {
+ node = tmp->data;
+ if (node == NULL)
+ break;
+
+ g_hash_table_insert(server->isupport, g_strdup(node->key),
+ g_strdup(node->value));
+ }
+
}
static void sig_session_restore_nick(IRC_CHANNEL_REC *channel,
CONFIG_NODE *node)
{
const char *nick;
+ char *other;
int op, halfop, voice;
NICK_REC *nickrec;
@@ -89,6 +128,8 @@ static void sig_session_restore_nick(IRC_CHANNEL_REC *channel,
voice = config_node_get_bool(node, "voice", FALSE);
halfop = config_node_get_bool(node, "halfop", FALSE);
nickrec = irc_nicklist_insert(channel, nick, op, halfop, voice, FALSE);
+ other = config_node_get_str(node, "other", FALSE);
+ nickrec->other = other[0];
}
static void session_restore_channel(IRC_CHANNEL_REC *channel)
diff --git a/src/irc/core/irc.h b/src/irc/core/irc.h
index 77d7dc98..5197f42d 100644
--- a/src/irc/core/irc.h
+++ b/src/irc/core/irc.h
@@ -19,9 +19,8 @@ typedef struct _REDIRECT_REC REDIRECT_REC;
((a) == '^' || (a) == '~' || \
(a) == '+' || (a) == '=' || (a) == '-')
-#define isnickflag(a) \
- ((a) == '@' || (a) == '+' || (a) == '%' || /* op / voice */ \
- (a) == '%' || (a) == '.' || (a) == '!') /* extensions: half-op / owners */
+#define isnickflag(server, a) \
+ (server->prefix[(int)(unsigned char) a] != '\0')
#define ischannel(a) \
((a) == '#' || /* normal */ \
diff --git a/src/irc/core/modes.c b/src/irc/core/modes.c
index ec41889b..159d375c 100644
--- a/src/irc/core/modes.c
+++ b/src/irc/core/modes.c
@@ -32,7 +32,7 @@
/* Change nick's mode in channel */
static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *nick,
- const char mode, int type, const char *setby)
+ char mode, int type, const char *setby)
{
NICK_REC *nickrec;
char modestr[2], typestr[2];
@@ -44,8 +44,10 @@ static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *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 == '+';
+ else if (mode == '+') nickrec->voice = type == '+';
+ else if (mode == '%') nickrec->halfop = type == '+';
+ else if (channel->server->prefix[(unsigned char) mode] != '\0')
+ nickrec->other = (type == '+' ? mode : '\0');
modestr[0] = mode; modestr[1] = '\0';
typestr[0] = type; typestr[1] = '\0';
@@ -97,13 +99,15 @@ static void mode_add_arg(GString *str, int pos, int updating, const char *arg)
}
/* Add mode character to list sorted alphabetically */
-static void mode_add_sorted(GString *str, char mode, const char *arg)
+static void mode_add_sorted(IRC_SERVER_REC *server, GString *str,
+ char mode, const char *arg, int user)
{
char *p;
int updating, argpos = 0;
/* check that mode isn't already set */
- if (!HAS_MODE_ARG_SET(mode) && mode_is_set(str->str, mode))
+ if ((!user && !HAS_MODE_ARG_SET(server, mode)) &&
+ mode_is_set(str->str, mode))
return;
updating = FALSE;
@@ -114,7 +118,7 @@ static void mode_add_sorted(GString *str, char mode, const char *arg)
updating = TRUE;
break;
}
- if (HAS_MODE_ARG_SET(*p))
+ if (!user && HAS_MODE_ARG_SET(server, *p))
argpos++;
}
@@ -154,7 +158,7 @@ static void node_remove_arg(GString *str, int pos)
}
/* remove mode (and it's argument) from string */
-static void mode_remove(GString *str, char mode)
+static void mode_remove(IRC_SERVER_REC *server, GString *str, char mode, int user)
{
char *p;
int argpos = 0;
@@ -162,34 +166,91 @@ static void mode_remove(GString *str, char mode)
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))
+ if (!user && HAS_MODE_ARG_SET(server, mode))
node_remove_arg(str, argpos);
break;
}
- if (HAS_MODE_ARG_SET(*p))
+ if (!user && HAS_MODE_ARG_SET(server, *p))
argpos++;
}
}
-static void mode_set(GString *str, char type, char mode)
+static void mode_set(IRC_SERVER_REC *server, GString *str,
+ char type, char mode, int user)
{
g_return_if_fail(str != NULL);
if (type == '-')
- mode_remove(str, mode);
+ mode_remove(server, str, mode, user);
else
- mode_add_sorted(str, mode, NULL);
+ mode_add_sorted(server, str, mode, NULL, user);
}
-static void mode_set_arg(GString *str, char type, char mode, const char *arg)
+static void mode_set_arg(IRC_SERVER_REC *server, 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);
+ mode_remove(server, str, mode, TRUE);
else
- mode_add_sorted(str, mode, arg);
+ mode_add_sorted(server, str, mode, arg, TRUE);
+}
+
+/* Mode that needs a parameter of a mask for both setting and removing (eg: bans) */
+void modes_type_a(IRC_CHANNEL_REC *channel, const char *setby, char type,
+ char mode, char *arg, GString *newmode)
+{
+ /* Currently only +b is dealt with */
+ if (mode == 'b') {
+ if (type == '+')
+ banlist_add(channel, arg, setby, time(NULL));
+ else
+ banlist_remove(channel, arg);
+ }
+}
+
+/* Mode that needs parameter for both setting and removing (eg: +k) */
+void modes_type_b(IRC_CHANNEL_REC *channel, const char *setby, char type,
+ char mode, char *arg, GString *newmode)
+{
+ if (mode == 'k') {
+ if (*arg == '\0' && type == '+')
+ arg = channel->key != NULL ? channel->key : "???";
+ mode_set_arg(channel->server, newmode, type, 'k', arg);
+
+ if (arg != channel->key) {
+ g_free_and_null(channel->key);
+ if (type == '+')
+ channel->key = g_strdup(arg);
+ }
+ }
+}
+
+/* Mode that needs parameter only for adding */
+void modes_type_c(IRC_CHANNEL_REC *channel, const char *setby,
+ char type, char mode, char *arg, GString *newmode)
+{
+ if (mode == 'l') {
+ mode_set_arg(channel->server, newmode, type, 'l', arg);
+ channel->limit = type == '-' ? 0 : atoi(arg);
+ }
+}
+
+/* Mode that takes no parameter */
+void modes_type_d(IRC_CHANNEL_REC *channel, const char *setby,
+ char type, char mode, char *arg, GString *newmode)
+{
+ mode_set(channel->server, newmode, type, mode, FALSE);
+}
+
+void modes_type_prefix(IRC_CHANNEL_REC *channel, const char *setby,
+ char type, char mode, char *arg, GString *newmode)
+{
+ int umode = (unsigned char) mode;
+ nick_mode_change(channel, arg, channel->server->modes[umode].prefix,
+ type, setby);
}
int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode)
@@ -204,8 +265,10 @@ int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode)
void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
const char *mode, int update_key)
{
+ IRC_SERVER_REC *server = channel->server;
GString *newmode;
char *dup, *modestr, *arg, *curmode, type;
+ int umode;
g_return_if_fail(IS_IRC_CHANNEL(channel));
g_return_if_fail(mode != NULL);
@@ -216,7 +279,7 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
dup = modestr = g_strdup(mode);
curmode = cmd_get_param(&modestr);
while (*curmode != '\0') {
- if (HAS_MODE_ARG(type, *curmode)) {
+ if (HAS_MODE_ARG(server, type, *curmode)) {
/* get the argument for the mode. NOTE: We don't
get the +k's argument when joining to channel. */
arg = cmd_get_param(&modestr);
@@ -229,54 +292,17 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
case '-':
type = *curmode;
break;
-
- case 'b':
- if (type == '+')
- banlist_add(channel, arg, setby, time(NULL));
- else
- banlist_remove(channel, arg);
- break;
- case 'o':
- case 'O': /* channel owner in !channels */
- if (g_strcasecmp(channel->server->nick, arg) == 0)
- channel->chanop = type == '+';
- nick_mode_change(channel, arg, '@', type, setby);
- break;
- case 'h':
- nick_mode_change(channel, arg, '%', type, setby);
- break;
- case 'v':
- nick_mode_change(channel, arg, '+', type, setby);
- break;
-
- case 'l':
- mode_set_arg(newmode, type, 'l', arg);
- channel->limit = type == '-' ? 0 : atoi(arg);
- break;
- case 'k':
- if ((*arg == '\0' && type == '+') ||
- (channel->key != NULL && !update_key)) {
- arg = channel->key != NULL ? channel->key :
- "???";
- }
- mode_set_arg(newmode, type, 'k', arg);
-
- if (arg != channel->key) {
- g_free_and_null(channel->key);
- if (type == '+')
- channel->key = g_strdup(arg);
- }
- break;
- case 'e':
- case 'I':
- case 'q':
- case 'd':
- /* Don't set it as channel mode */
- break;
-
default:
- mode_set(newmode, type, *curmode);
- break;
+ umode = (unsigned char) *curmode;
+ if (server->modes[umode].func != NULL) {
+ server->modes[umode].func(channel, setby,
+ type, *curmode, arg,
+ newmode);
+ } else {
+ /* Treat unknown modes as ones without params */
+ modes_type_d(channel, setby, type, *curmode,
+ arg, newmode);
+ }
}
curmode++;
@@ -305,7 +331,8 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
/* 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)
+char *modes_join(IRC_SERVER_REC *server, const char *old,
+ const char *mode, int channel)
{
GString *newmode;
char *dup, *modestr, *curmode, type;
@@ -324,10 +351,10 @@ char *modes_join(const char *old, const char *mode, int channel)
continue;
}
- if (!channel || !HAS_MODE_ARG(type, *curmode))
- mode_set(newmode, type, *curmode);
+ if (!channel || !HAS_MODE_ARG(server, type, *curmode))
+ mode_set(server, newmode, type, *curmode, !channel);
else {
- mode_set_arg(newmode, type, *curmode,
+ mode_set_arg(server, newmode, type, *curmode,
cmd_get_param(&modestr));
}
@@ -348,7 +375,7 @@ static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
g_return_if_fail(IS_IRC_SERVER(server));
g_return_if_fail(modestr != NULL);
- newmode = modes_join(server->usermode, modestr, FALSE);
+ newmode = modes_join(NULL, server->usermode, modestr, FALSE);
oldmode = server->usermode;
server->usermode = newmode;
server->server_operator = (strchr(newmode, 'o') != NULL);
@@ -430,7 +457,7 @@ static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
&target, &mode);
if (!ischannel(*target)) {
/* we requested a user mode change, save this */
- mode = modes_join(server->wanted_usermode, mode, FALSE);
+ mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
g_free_not_null(server->wanted_usermode);
server->wanted_usermode = mode;
}
@@ -520,7 +547,7 @@ void channel_set_mode(IRC_SERVER_REC *server, const char *channel,
}
if (count == server->max_modes_in_cmd &&
- HAS_MODE_ARG(type, *curmode)) {
+ HAS_MODE_ARG(chanrec->server, type, *curmode)) {
irc_send_cmdv(server, "MODE %s %s%s",
channel, tmode->str, targs->str);
@@ -535,7 +562,7 @@ void channel_set_mode(IRC_SERVER_REC *server, const char *channel,
}
g_string_append_c(tmode, *curmode);
- if (HAS_MODE_ARG(type, *curmode)) {
+ if (HAS_MODE_ARG(chanrec->server, type, *curmode)) {
char *arg;
count++;
@@ -762,6 +789,29 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
cmd_params_free(free_arg);
}
+void modes_server_init(IRC_SERVER_REC *server)
+{
+ server->modes['b'].func = modes_type_a;
+ server->modes['e'].func = modes_type_a;
+ server->modes['I'].func = modes_type_a;
+
+ server->modes['h'].func = modes_type_prefix;
+ server->modes['h'].prefix = '%';
+ server->modes['o'].func = modes_type_prefix;
+ server->modes['o'].prefix = '@';
+ server->modes['O'].func = modes_type_prefix;
+ server->modes['O'].prefix = '@';
+ server->modes['v'].func = modes_type_prefix;
+ server->modes['v'].prefix = '+';
+
+ server->prefix['%'] = 'h';
+ server->prefix['@'] = 'o';
+ server->prefix['+'] = 'v';
+
+ server->modes['l'].func = modes_type_b;
+ server->modes['k'].func = modes_type_c;
+}
+
void modes_init(void)
{
settings_add_str("misc", "opermode", "");
diff --git a/src/irc/core/modes.h b/src/irc/core/modes.h
index 485b176b..d5af8403 100644
--- a/src/irc/core/modes.h
+++ b/src/irc/core/modes.h
@@ -1,30 +1,43 @@
#ifndef __MODES_H
#define __MODES_H
+#include "irc-channels.h"
+
+typedef void mode_func_t(IRC_CHANNEL_REC *, const char *, char, char,
+ char *, GString *);
+
+struct modes_type {
+ mode_func_t *func;
+ char prefix;
+};
+
/* modes that have argument always */
-#define HAS_MODE_ARG_ALWAYS(mode) \
- ((mode) == 'b' || (mode) == 'e' || (mode) == 'I' || (mode) == 'q' || \
- (mode) == 'd' || (mode) == 'o' || (mode) == 'h' || (mode) == 'v' || \
- (mode) == 'O' || (mode) == 'k' || (mode) == 'f')
+#define HAS_MODE_ARG_ALWAYS(server, mode) \
+ (server->modes[(int)(unsigned char) mode].func == modes_type_a || \
+ server->modes[(int)(unsigned char) mode].func == modes_type_b || \
+ server->modes[(int)(unsigned char) mode].func == modes_type_prefix)
/* modes that have argument when being set (+) */
-#define HAS_MODE_ARG_SET(mode) \
- (HAS_MODE_ARG_ALWAYS(mode) || (mode) == 'l')
+#define HAS_MODE_ARG_SET(server, mode) \
+ (HAS_MODE_ARG_ALWAYS(server, mode) || \
+ server->modes[(int)(unsigned char) mode].func == modes_type_c)
/* modes that have argument when being unset (-) */
-#define HAS_MODE_ARG_UNSET(mode) \
- HAS_MODE_ARG_ALWAYS(mode)
+#define HAS_MODE_ARG_UNSET(server, mode) \
+ HAS_MODE_ARG_ALWAYS(server, mode)
-#define HAS_MODE_ARG(type, mode) \
- ((type) == '+' ? HAS_MODE_ARG_SET(mode) : HAS_MODE_ARG_UNSET(mode))
+#define HAS_MODE_ARG(server, type, mode) \
+ ((type) == '+' ? HAS_MODE_ARG_SET(server,mode) : \
+ HAS_MODE_ARG_UNSET(server, mode))
void modes_init(void);
void modes_deinit(void);
+void modes_server_init(IRC_SERVER_REC *);
/* 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);
+char *modes_join(IRC_SERVER_REC *server, const char *old, const char *mode, int channel);
int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode);
@@ -36,4 +49,10 @@ void channel_set_singlemode(IRC_CHANNEL_REC *channel, const char *nicks,
void channel_set_mode(IRC_SERVER_REC *server, const char *channel,
const char *mode);
+mode_func_t modes_type_a;
+mode_func_t modes_type_b;
+mode_func_t modes_type_c;
+mode_func_t modes_type_d;
+mode_func_t modes_type_prefix;
+
#endif
diff --git a/src/irc/core/netsplit.c b/src/irc/core/netsplit.c
index 04eb3ff0..0e85b44c 100644
--- a/src/irc/core/netsplit.c
+++ b/src/irc/core/netsplit.c
@@ -135,6 +135,7 @@ static NETSPLIT_REC *netsplit_add(IRC_SERVER_REC *server, const char *nick,
splitchan->op = nickrec->op;
splitchan->halfop = nickrec->halfop;
splitchan->voice = nickrec->voice;
+ splitchan->other = nickrec->other;
rec->channels = g_slist_append(rec->channels, splitchan);
}
diff --git a/src/irc/core/netsplit.h b/src/irc/core/netsplit.h
index 9b28c9c6..23e6d636 100644
--- a/src/irc/core/netsplit.h
+++ b/src/irc/core/netsplit.h
@@ -28,6 +28,7 @@ typedef struct {
unsigned int op:1;
unsigned int halfop:1;
unsigned int voice:1;
+ unsigned int other:7;
} NETSPLIT_CHAN_REC;
void netsplit_init(void);