diff --git a/INSTALL b/INSTALL index 712678e4..3dbe2ebd 100644 --- a/INSTALL +++ b/INSTALL @@ -5,7 +5,7 @@ To compile Irssi you need: - meson-0.49 build system with ninja-1.5 or greater -- glib-2.28 or greater +- glib-2.32 or greater - openssl (for ssl support) - perl-5.6 or greater (for Perl support) - terminfo or ncurses (for text frontend) diff --git a/README.md b/README.md index 8b857eeb..08be65b4 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ make && sudo make install ### Requirements -- [glib-2.28](https://wiki.gnome.org/Projects/GLib) or greater +- [glib-2.32](https://wiki.gnome.org/Projects/GLib) or greater - [openssl](https://www.openssl.org/) - [perl-5.6](https://www.perl.org/) or greater (for perl support) - terminfo or ncurses (for text frontend) diff --git a/configure.ac b/configure.ac index cafd2970..41a053fd 100644 --- a/configure.ac +++ b/configure.ac @@ -273,7 +273,7 @@ for try in 1 2; do echo "*** trying without -lgmodule" glib_modules= fi - AM_PATH_GLIB_2_0(2.28.0,,, $glib_modules) + AM_PATH_GLIB_2_0(2.32.0,,, $glib_modules) if test "$GLIB_LIBS"; then if test $glib_modules = gmodule; then AC_DEFINE(HAVE_GMODULE) diff --git a/meson.build b/meson.build index f54db548..bd076a4d 100644 --- a/meson.build +++ b/meson.build @@ -166,7 +166,7 @@ message('*** Or alternatively install your distribution\'s package') message('*** On Debian: sudo apt-get install libglib2.0-dev') message('*** On Redhat: dnf install glib2-devel') if not require_glib_internal - glib_dep = dependency('glib-2.0', version : '>=2.28', required : not want_glib_internal, static : want_static_dependency) + glib_dep = dependency('glib-2.0', version : '>=2.32', required : not want_glib_internal, static : want_static_dependency) else glib_dep = dependency('', required : false) endif diff --git a/src/common.h b/src/common.h index 39045de7..ccd000d5 100644 --- a/src/common.h +++ b/src/common.h @@ -6,7 +6,7 @@ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ -#define IRSSI_ABI_VERSION 34 +#define IRSSI_ABI_VERSION 35 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697 diff --git a/src/fe-common/irc/fe-events-numeric.c b/src/fe-common/irc/fe-events-numeric.c index 37fdf30a..f78289c0 100644 --- a/src/fe-common/irc/fe-events-numeric.c +++ b/src/fe-common/irc/fe-events-numeric.c @@ -724,6 +724,7 @@ void fe_events_numeric_init(void) signal_add("event 004", (SIGNAL_FUNC) event_received); signal_add("event 005", (SIGNAL_FUNC) event_received); signal_add("event 254", (SIGNAL_FUNC) event_received); + signal_add("event 354", (SIGNAL_FUNC) event_received); signal_add("event 364", (SIGNAL_FUNC) event_received); signal_add("event 365", (SIGNAL_FUNC) event_received); signal_add("event 381", (SIGNAL_FUNC) event_received); @@ -814,6 +815,7 @@ void fe_events_numeric_deinit(void) signal_remove("event 004", (SIGNAL_FUNC) event_received); signal_remove("event 005", (SIGNAL_FUNC) event_received); signal_remove("event 254", (SIGNAL_FUNC) event_received); + signal_remove("event 354", (SIGNAL_FUNC) event_received); signal_remove("event 364", (SIGNAL_FUNC) event_received); signal_remove("event 365", (SIGNAL_FUNC) event_received); signal_remove("event 381", (SIGNAL_FUNC) event_received); diff --git a/src/fe-common/irc/fe-whois.c b/src/fe-common/irc/fe-whois.c index 9a31cd9e..b0eeb19a 100644 --- a/src/fe-common/irc/fe-whois.c +++ b/src/fe-common/irc/fe-whois.c @@ -415,6 +415,7 @@ void fe_whois_init(void) signal_add("event 319", (SIGNAL_FUNC) event_whois_channels); signal_add("event 313", (SIGNAL_FUNC) event_whois_oper); signal_add("event 330", (SIGNAL_FUNC) event_whois_auth); + signal_add("whois account", (SIGNAL_FUNC) event_whois_auth); signal_add("event 377", (SIGNAL_FUNC) event_whois_usermode); signal_add("event 378", (SIGNAL_FUNC) event_whois_realhost); signal_add("event 379", (SIGNAL_FUNC) event_whois_modes); @@ -438,6 +439,7 @@ void fe_whois_deinit(void) signal_remove("event 319", (SIGNAL_FUNC) event_whois_channels); signal_remove("event 313", (SIGNAL_FUNC) event_whois_oper); signal_remove("event 330", (SIGNAL_FUNC) event_whois_auth); + signal_remove("whois account", (SIGNAL_FUNC) event_whois_auth); signal_remove("event 377", (SIGNAL_FUNC) event_whois_usermode); signal_remove("event 378", (SIGNAL_FUNC) event_whois_realhost); signal_remove("event 379", (SIGNAL_FUNC) event_whois_modes); diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c index 8f90af64..97e982b7 100644 --- a/src/irc/core/channels-query.c +++ b/src/irc/core/channels-query.c @@ -50,22 +50,21 @@ loop: #include #include -enum { - CHANNEL_QUERY_MODE, - CHANNEL_QUERY_WHO, - CHANNEL_QUERY_BMODE, +/* here are the WHOX commands we send. the full spec can be found on [1]. - CHANNEL_QUERIES -}; + (1) WHOX_CHANNEL_FULL_CMD for getting the user list when we join a channel. we request the fields + c (channel), u (user), h (host), n (nick), f (flags), d (hops), a (important, account!), and + r (the real name goes last because it os the only that can contain spaces.) we request all + those fields as they are also included in the "regular" WHO reply we would get without WHOX. -#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO) + (2) WHOX_USERACCOUNT_CMD for getting the account names of people that joined. this code is + obviously only used when we don't have extended-joins. we request n (nick) and a (account) + only, and we only send WHO nick with this command. -typedef struct { - int current_query_type; /* query type that is currently being asked */ - GSList *current_queries; /* All channels that are currently being queried */ - - GSList *queries[CHANNEL_QUERIES]; /* All queries that need to be asked from server */ -} SERVER_QUERY_REC; + [1] https://github.com/UndernetIRC/ircu2/blob/u2_10_12_branch/doc/readme.who + */ +#define WHOX_CHANNEL_FULL_CMD "WHO %s %%tcuhnfdar," WHOX_CHANNEL_FULL_ID +#define WHOX_USERACCOUNT_CMD "WHO %s %%tna," WHOX_USERACCOUNT_ID static void sig_connected(IRC_SERVER_REC *server) { @@ -76,7 +75,9 @@ static void sig_connected(IRC_SERVER_REC *server) return; rec = g_new0(SERVER_QUERY_REC, 1); - server->chanqueries = rec; + rec->accountqueries = g_hash_table_new_full( + (GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal, (GDestroyNotify) g_free, NULL); + server->chanqueries = rec; } static void sig_disconnected(IRC_SERVER_REC *server) @@ -91,6 +92,7 @@ static void sig_disconnected(IRC_SERVER_REC *server) rec = server->chanqueries; g_return_if_fail(rec != NULL); + g_hash_table_destroy(rec->accountqueries); for (n = 0; n < CHANNEL_QUERIES; n++) g_slist_free(rec->queries[n]); g_slist_free(rec->current_queries); @@ -229,15 +231,19 @@ static void query_send(IRC_SERVER_REC *server, int query) break; case CHANNEL_QUERY_WHO: - cmd = g_strdup_printf("WHO %s", chanstr_commas); + if (server->isupport != NULL && + g_hash_table_lookup(server->isupport, "whox") != NULL) { + cmd = g_strdup_printf(WHOX_CHANNEL_FULL_CMD, chanstr_commas); + } else { + cmd = g_strdup_printf("WHO %s", chanstr_commas); + } - server_redirect_event(server, "who", - server->one_endofwho ? 1 : count, - chanstr, -1, - "chanquery abort", - "event 315", "chanquery who end", - "event 352", "silent event who", - "", "chanquery abort", NULL); + server_redirect_event(server, "who", server->one_endofwho ? 1 : count, chanstr, -1, + "chanquery abort", /* failure signal */ + "event 315", "chanquery who end", /* */ + "event 352", "silent event who", /* */ + "event 354", "silent event whox", /* */ + "", "chanquery abort", NULL); break; case CHANNEL_QUERY_BMODE: @@ -395,6 +401,146 @@ static void channel_got_query(IRC_CHANNEL_REC *chanrec, int query_type) query_check(chanrec->server); } +void irc_channels_query_purge_accountquery(IRC_SERVER_REC *server, const char *nick) +{ + GSList *tmp, *next, *prev; + REDIRECT_REC *redirect; + char *cmd, *target_cmd; + gboolean was_removed; + + /* remove the marker */ + was_removed = g_hash_table_remove(server->chanqueries->accountqueries, nick); + + /* if it was removed we may have an outstanding query */ + if (was_removed) { + target_cmd = g_strdup_printf(WHOX_USERACCOUNT_CMD "\r\n", nick); + + /* remove queued WHO command */ + prev = NULL; + for (tmp = server->cmdqueue; tmp != NULL; tmp = next) { + next = tmp->next->next; + cmd = tmp->data; + redirect = tmp->next->data; + + if (g_strcmp0(cmd, target_cmd) == 0) { + if (prev != NULL) + prev->next = next; + else + server->cmdqueue = next; + + /* remove the redirection */ + g_slist_free_1(tmp->next); + if (redirect != NULL) + server_redirect_destroy(redirect); + + /* remove the command */ + g_slist_free_1(tmp); + g_free(cmd); + + server->cmdcount--; + } else { + prev = tmp->next; + } + } + + g_free(target_cmd); + } +} + +static void query_useraccount_error(IRC_SERVER_REC *server, const char *cmd, const char *arg) +{ + /* query failed, ignore it but remove the marker */ + g_hash_table_remove(server->chanqueries->accountqueries, arg); +} + +static void sig_event_join(IRC_SERVER_REC *server, const char *data, const char *nick, + const char *address) +{ + char *params, *channel, *ptr, *account; + GSList *nicks, *tmp; + IRC_CHANNEL_REC *chanrec; + NICK_REC *nickrec; + + g_return_if_fail(data != NULL); + + if (i_slist_find_string(server->cap_active, CAP_EXTENDED_JOIN)) { + /* no need to chase accounts */ + return; + } + + if (g_ascii_strcasecmp(nick, server->nick) == 0) { + /* You joined, do nothing */ + return; + } + + params = event_get_params(data, 3, &channel, NULL, NULL); + + ptr = strchr(channel, 7); /* ^G does something weird.. */ + if (ptr != NULL) + *ptr = '\0'; + + /* find channel */ + chanrec = irc_channel_find(server, channel); + if (chanrec == NULL) { + g_free(params); + return; + } + + g_free(params); + + if (!chanrec->wholist) { + return; + } + + /* find nick */ + nickrec = nicklist_find(CHANNEL(chanrec), nick); + if (nickrec == NULL) { + return; + } + + if (nickrec->account != NULL) { + return; + } + + if (g_hash_table_contains(server->chanqueries->accountqueries, nick)) { + /* query already sent */ + return; + } + account = NULL; + + /* Check if user is already in some other channel, get the account from there */ + nicks = nicklist_get_same(SERVER(server), nick); + for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) { + NICK_REC *rec = tmp->next->data; + + if (rec->account != NULL) { + account = rec->account; + break; + } + } + g_slist_free(nicks); + + if (account != NULL) { + nicklist_set_account(CHANNEL(chanrec), nickrec, account); + return; + } + + if (g_hash_table_size(chanrec->nicks) < settings_get_int("channel_max_who_sync") && + server->isupport != NULL && g_hash_table_lookup(server->isupport, "whox") != NULL) { + char *cmd; + server_redirect_event(server, "who user", 1, nick, -1, + "chanquery useraccount abort", /* failure signal */ + "event 354", "silent event whox useraccount", /* */ + "", "event empty", /* */ + NULL); + cmd = g_strdup_printf(WHOX_USERACCOUNT_CMD, nick); + g_hash_table_add(server->chanqueries->accountqueries, g_strdup(nick)); + /* queue the command */ + irc_send_cmd_full(server, cmd, FALSE, FALSE, FALSE); + g_free(cmd); + } +} + static void event_channel_mode(IRC_SERVER_REC *server, const char *data, const char *nick) { @@ -493,11 +639,14 @@ void channels_query_init(void) signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined); signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_add("event join", (SIGNAL_FUNC) sig_event_join); + signal_add("chanquery mode", (SIGNAL_FUNC) event_channel_mode); signal_add("chanquery who end", (SIGNAL_FUNC) event_end_of_who); signal_add("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist); signal_add("chanquery abort", (SIGNAL_FUNC) query_current_error); + signal_add("chanquery useraccount abort", (SIGNAL_FUNC) query_useraccount_error); } void channels_query_deinit(void) @@ -507,9 +656,12 @@ void channels_query_deinit(void) signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined); signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); + signal_remove("event join", (SIGNAL_FUNC) sig_event_join); + signal_remove("chanquery mode", (SIGNAL_FUNC) event_channel_mode); signal_remove("chanquery who end", (SIGNAL_FUNC) event_end_of_who); signal_remove("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist); signal_remove("chanquery abort", (SIGNAL_FUNC) query_current_error); + signal_remove("chanquery useraccount abort", (SIGNAL_FUNC) query_useraccount_error); } diff --git a/src/irc/core/irc-channels.h b/src/irc/core/irc-channels.h index 4ed7fd73..0f955dd8 100644 --- a/src/irc/core/irc-channels.h +++ b/src/irc/core/irc-channels.h @@ -11,6 +11,20 @@ #define IS_IRC_CHANNEL(channel) \ (IRC_CHANNEL(channel) ? TRUE : FALSE) +enum { + CHANNEL_QUERY_MODE, + CHANNEL_QUERY_WHO, + CHANNEL_QUERY_BMODE, + + CHANNEL_QUERIES +}; + +/* arbitrary 3-digit identifiers so we can find our WHOX responses */ +#define WHOX_CHANNEL_FULL_ID "743" +#define WHOX_USERACCOUNT_ID "745" + +#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO) + #define STRUCT_SERVER_REC IRC_SERVER_REC struct _IRC_CHANNEL_REC { #include @@ -22,6 +36,16 @@ struct _IRC_CHANNEL_REC { int last_massjoins; /* Massjoins when last checked in timeout function */ }; +typedef struct _SERVER_QUERY_REC { + int current_query_type; /* query type that is currently being asked */ + GSList *current_queries; /* All channels that are currently being queried */ + + GSList *queries[CHANNEL_QUERIES]; /* All queries that need to be asked from server */ + GHashTable *accountqueries; /* Per-nick account queries */ +} SERVER_QUERY_REC; + +void irc_channels_query_purge_accountquery(IRC_SERVER_REC *server, const char *nick); + void irc_channels_init(void); void irc_channels_deinit(void); diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c index f6c25641..935c6b42 100644 --- a/src/irc/core/irc-commands.c +++ b/src/irc/core/irc-commands.c @@ -438,15 +438,18 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server, query = get_redirect_nicklist(query, &free_nick); str = g_strconcat(qserver, " ", query, NULL); - server_redirect_event(server, "whois", 1, str, TRUE, - NULL, - "event 318", "whois end", - "event 402", event_402, - "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */ - "event 313", "whois oper", - "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"), - "event 311", "whois event", - "", "whois default event", NULL); + server_redirect_event( + server, "whois", 1, str, TRUE, /* */ + NULL, /* */ + "event 318", "whois end", /* */ + "event 402", event_402, /* */ + "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */ + "event 313", "whois oper", /* */ + "event 330", "whois account", /* */ + "event 401", + (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"), + "event 311", "whois event", /* */ + "", "whois default event", NULL); g_free(str); server->whois_found = FALSE; diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c index cc630a58..f62a96f2 100644 --- a/src/irc/core/irc-nicklist.c +++ b/src/irc/core/irc-nicklist.c @@ -29,6 +29,34 @@ #include #include +static void nicklist_set_modes(IRC_CHANNEL_REC *channel, NICK_REC *rec, gboolean op, + gboolean halfop, gboolean voice, const char *prefixes, + gboolean send_changed) +{ + gboolean changed = FALSE; + if (rec->op != op) { + rec->op = op; + changed = TRUE; + } + if (rec->halfop != halfop) { + rec->halfop = halfop; + changed = TRUE; + } + if (rec->voice != voice) { + rec->voice = voice; + changed = TRUE; + } + + if (prefixes != NULL && g_strcmp0(rec->prefixes, prefixes) != 0) { + g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes)); + changed = TRUE; + } + + if (changed && send_changed) { + signal_emit("nicklist changed", 3, channel, rec, rec->nick); + } +} + /* Add new nick to list */ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick, int op, int halfop, int voice, int send_massjoin, @@ -42,14 +70,8 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick, rec = g_new0(NICK_REC, 1); rec->nick = g_strdup(nick); - if (op) rec->op = TRUE; - if (halfop) rec->halfop = TRUE; - if (voice) rec->voice = TRUE; rec->send_massjoin = send_massjoin; - - if (prefixes != NULL) { - g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes)); - } + nicklist_set_modes(channel, rec, op, halfop, voice, prefixes, FALSE); nicklist_insert(CHANNEL(channel), rec); return rec; @@ -154,11 +176,14 @@ static void event_names_list(IRC_SERVER_REC *server, const char *data) if (host != NULL) *host++ = '\0'; - if (nicklist_find((CHANNEL_REC *) chanrec, ptr) == NULL) { + rec = nicklist_find((CHANNEL_REC *) chanrec, ptr); + if (rec == NULL) { rec = irc_nicklist_insert(chanrec, ptr, op, halfop, voice, FALSE, prefixes); if (host != NULL) nicklist_set_host(CHANNEL(chanrec), rec, host); + } else { + nicklist_set_modes(chanrec, rec, op, halfop, voice, prefixes, TRUE); } } @@ -196,23 +221,13 @@ static void event_end_of_names(IRC_SERVER_REC *server, const char *data) g_free(params); } -static void event_who(SERVER_REC *server, const char *data) +static void fill_who(SERVER_REC *server, const char *channel, const char *user, const char *host, + const char *nick, const char *stat, const char *hops, const char *account, + const char *realname) { - char *params, *nick, *channel, *user, *host, *stat, *realname, *hops; CHANNEL_REC *chanrec; NICK_REC *nickrec; - g_return_if_fail(data != NULL); - - params = event_get_params(data, 8, NULL, &channel, &user, &host, - NULL, &nick, &stat, &realname); - - /* get hop count */ - hops = realname; - while (*realname != '\0' && *realname != ' ') realname++; - if (*realname == ' ') - *realname++ = '\0'; - /* update host, realname, hopcount */ chanrec = channel_find(server, channel); nickrec = chanrec == NULL ? NULL : @@ -223,15 +238,89 @@ static void event_who(SERVER_REC *server, const char *data) nicklist_set_host(chanrec, nickrec, str); g_free(str); } - if (nickrec->realname == NULL) + if (nickrec->realname == NULL) { nickrec->realname = g_strdup(realname); + } + if (nickrec->account == NULL && account != NULL) { + nicklist_set_account(chanrec, nickrec, + strcmp(account, "0") == 0 ? "*" : account); + } sscanf(hops, "%d", &nickrec->hops); } nicklist_update_flags(server, nick, strchr(stat, 'G') != NULL, /* gone */ strchr(stat, '*') != NULL); /* ircop */ +} +static void event_who(SERVER_REC *server, const char *data) +{ + char *params, *nick, *channel, *user, *host, *stat, *realname, *hops; + + g_return_if_fail(data != NULL); + + params = + event_get_params(data, 8, NULL, &channel, &user, &host, NULL, &nick, &stat, &realname); + + /* get hop count */ + hops = realname; + while (*realname != '\0' && *realname != ' ') + realname++; + if (*realname == ' ') + *realname++ = '\0'; + + fill_who(server, channel, user, host, nick, stat, hops, NULL, realname); + + g_free(params); +} + +static void event_whox_channel_full(SERVER_REC *server, const char *data) +{ + char *params, *id, *nick, *channel, *user, *host, *stat, *hops, *account, *realname; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 10, NULL, &id, &channel, &user, &host, &nick, &stat, &hops, + &account, &realname); + + if (g_strcmp0(id, WHOX_CHANNEL_FULL_ID) != 0) { + g_free(params); + return; + } + + fill_who(server, channel, user, host, nick, stat, hops, account, realname); + + g_free(params); +} + +static void event_whox_useraccount(IRC_SERVER_REC *server, const char *data) +{ + char *params, *id, *nick, *account; + GSList *nicks, *tmp; + + g_return_if_fail(data != NULL); + + params = event_get_params(data, 4, NULL, &id, &nick, &account); + + if (g_strcmp0(id, WHOX_USERACCOUNT_ID) != 0) { + g_free(params); + return; + } + g_hash_table_remove(server->chanqueries->accountqueries, nick); + + if (strcmp(account, "0") == 0) { + account = "*"; + } + + nicks = nicklist_get_same(SERVER(server), nick); + for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) { + NICK_REC *rec = tmp->next->data; + + if (rec->account == NULL || g_strcmp0(rec->account, account) != 0) { + nicklist_set_account(CHANNEL(tmp->data), rec, account); + } + } + g_slist_free(nicks); g_free(params); } @@ -415,7 +504,9 @@ static void event_nick(IRC_SERVER_REC *server, const char *data, server_change_nick(SERVER(server), nick); } - nicklist_rename(SERVER(server), orignick, nick); + /* invalidate any outstanding accountqueries for the old nick */ + irc_channels_query_purge_accountquery(server, orignick); + nicklist_rename(SERVER(server), orignick, nick); g_free(params); } @@ -510,7 +601,10 @@ void irc_nicklist_init(void) { signal_add_first("event nick", (SIGNAL_FUNC) event_nick); signal_add_first("event 352", (SIGNAL_FUNC) event_who); + signal_add_first("event 354", (SIGNAL_FUNC) event_whox_channel_full); signal_add("silent event who", (SIGNAL_FUNC) event_who); + signal_add("silent event whox", (SIGNAL_FUNC) event_whox_channel_full); + signal_add("silent event whox useraccount", (SIGNAL_FUNC) event_whox_useraccount); signal_add("silent event whois", (SIGNAL_FUNC) event_whois); signal_add_first("event 311", (SIGNAL_FUNC) event_whois); signal_add_first("whois away", (SIGNAL_FUNC) event_whois_away); @@ -534,7 +628,10 @@ void irc_nicklist_deinit(void) { signal_remove("event nick", (SIGNAL_FUNC) event_nick); signal_remove("event 352", (SIGNAL_FUNC) event_who); + signal_remove("event 354", (SIGNAL_FUNC) event_whox_channel_full); signal_remove("silent event who", (SIGNAL_FUNC) event_who); + signal_remove("silent event whox", (SIGNAL_FUNC) event_whox_channel_full); + signal_remove("silent event whox useraccount", (SIGNAL_FUNC) event_whox_useraccount); signal_remove("silent event whois", (SIGNAL_FUNC) event_whois); signal_remove("event 311", (SIGNAL_FUNC) event_whois); signal_remove("whois away", (SIGNAL_FUNC) event_whois_away); diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index 4833972a..14560611 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -136,7 +136,7 @@ struct _IRC_SERVER_REC { GSList *rejoin_channels; /* try to join to these channels after a while - channels go here if they're "temporarily unavailable" because of netsplits */ - void *chanqueries; + struct _SERVER_QUERY_REC *chanqueries; GHashTable *isupport; struct modes_type modes[256]; /* Stores the modes sent by a server in an isupport reply */ diff --git a/src/irc/core/massjoin.c b/src/irc/core/massjoin.c index 2405f947..61e2615f 100644 --- a/src/irc/core/massjoin.c +++ b/src/irc/core/massjoin.c @@ -39,12 +39,15 @@ static void event_join(IRC_SERVER_REC *server, const char *data, IRC_CHANNEL_REC *chanrec; NICK_REC *nickrec; GSList *nicks, *tmp; + gboolean send_massjoin; g_return_if_fail(data != NULL); if (g_ascii_strcasecmp(nick, server->nick) == 0) { - /* You joined, no need to do anything here */ - return; + /* You joined, do not massjoin */ + send_massjoin = FALSE; + } else { + send_massjoin = TRUE; } params = event_get_params(data, 3, &channel, &account, &realname); @@ -68,14 +71,14 @@ static void event_join(IRC_SERVER_REC *server, const char *data, } /* add user to nicklist */ - nickrec = irc_nicklist_insert(chanrec, nick, FALSE, FALSE, FALSE, TRUE, NULL); + nickrec = irc_nicklist_insert(chanrec, nick, FALSE, FALSE, FALSE, send_massjoin, NULL); if (*account != '\0' && g_strcmp0(nickrec->account, account) != 0) { nicklist_set_account(CHANNEL(chanrec), nickrec, account); } nicklist_set_host(CHANNEL(chanrec), nickrec, address); - if (chanrec->massjoins == 0) { + if (send_massjoin && chanrec->massjoins == 0) { /* no nicks waiting in massjoin queue */ chanrec->massjoin_start = time(NULL); chanrec->last_massjoins = 0; @@ -104,7 +107,9 @@ static void event_join(IRC_SERVER_REC *server, const char *data, nickrec->realname = g_strdup(realname); } - chanrec->massjoins++; + if (send_massjoin) { + chanrec->massjoins++; + } g_free(params); } @@ -217,6 +222,9 @@ static void event_quit(IRC_SERVER_REC *server, const char *data, nicklist_remove(CHANNEL(channel), nickrec); } g_slist_free(nicks); + + /* invalidate any outstanding accountqueries for the nick */ + irc_channels_query_purge_accountquery(server, nick); } static void event_kick(IRC_SERVER_REC *server, const char *data) diff --git a/src/irc/core/servers-redirect.c b/src/irc/core/servers-redirect.c index 72601d34..50560883 100644 --- a/src/irc/core/servers-redirect.c +++ b/src/irc/core/servers-redirect.c @@ -442,7 +442,7 @@ static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec) g_free(str); if (rec->failure_signal != NULL) - signal_emit(rec->failure_signal, 1, server); + signal_emit(rec->failure_signal, 3, server, rec->cmd->name, rec->arg); } else if (rec->last_signal != NULL) { /* emit the last signal */ signal_emit(rec->last_signal, 1, server); @@ -697,7 +697,16 @@ void servers_redirect_init(void) NULL, NULL); - /* LIST */ + /* WHO user */ + server_redirect_register("who user", FALSE, 0, /* */ + "event 352", 5, /* An element of the WHO */ + "event 354", -1, /* WHOX element */ + NULL, /* */ + "event 315", 1, /* End of WHO */ + NULL, /* */ + NULL); + + /* LIST */ server_redirect_register("list", FALSE, 0, "event 321", 1, /* Begins the LIST */ NULL,