diff options
Diffstat (limited to 'src/irc/core')
-rw-r--r-- | src/irc/core/irc-commands.c | 43 | ||||
-rw-r--r-- | src/irc/core/irc-expandos.c | 11 | ||||
-rw-r--r-- | src/irc/core/irc-nicklist.c | 54 | ||||
-rw-r--r-- | src/irc/core/irc-nicklist.h | 7 | ||||
-rw-r--r-- | src/irc/core/irc-queries.c | 15 | ||||
-rw-r--r-- | src/irc/core/irc-queries.h | 3 | ||||
-rw-r--r-- | src/irc/core/irc-servers.c | 147 | ||||
-rw-r--r-- | src/irc/core/irc-servers.h | 8 | ||||
-rw-r--r-- | src/irc/core/irc-session.c | 41 | ||||
-rw-r--r-- | src/irc/core/irc.h | 5 | ||||
-rw-r--r-- | src/irc/core/modes.c | 192 | ||||
-rw-r--r-- | src/irc/core/modes.h | 41 | ||||
-rw-r--r-- | src/irc/core/netsplit.c | 1 | ||||
-rw-r--r-- | src/irc/core/netsplit.h | 1 |
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); |