diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c index 8bf60587..9e284754 100644 --- a/src/irc/core/channels-query.c +++ b/src/irc/core/channels-query.c @@ -50,23 +50,6 @@ loop: #include #include -enum { - CHANNEL_QUERY_MODE, - CHANNEL_QUERY_WHO, - CHANNEL_QUERY_BMODE, - - CHANNEL_QUERIES -}; - -#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO) - -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; - static void sig_connected(IRC_SERVER_REC *server) { SERVER_QUERY_REC *rec; @@ -76,7 +59,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 +76,8 @@ static void sig_disconnected(IRC_SERVER_REC *server) rec = server->chanqueries; g_return_if_fail(rec != NULL); + g_hash_table_foreach(rec->accountqueries, (GHFunc) g_free, 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); @@ -399,6 +386,12 @@ static void channel_got_query(IRC_CHANNEL_REC *chanrec, int query_type) query_check(chanrec->server); } +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) { @@ -448,6 +441,10 @@ static void sig_event_join(IRC_SERVER_REC *server, const char *data, const char 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 */ @@ -470,11 +467,13 @@ static void sig_event_join(IRC_SERVER_REC *server, const char *data, const char 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, NULL, /* failure signal */ + 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("WHO %s %%tna,745", nick); + g_hash_table_add(server->chanqueries->accountqueries, g_strdup(nick)); irc_send_cmd(server, cmd); g_free(cmd); } @@ -585,6 +584,7 @@ void channels_query_init(void) 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) @@ -601,4 +601,5 @@ void channels_query_deinit(void) 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..769ddf2f 100644 --- a/src/irc/core/irc-channels.h +++ b/src/irc/core/irc-channels.h @@ -11,6 +11,16 @@ #define IS_IRC_CHANNEL(channel) \ (IRC_CHANNEL(channel) ? TRUE : FALSE) +enum { + CHANNEL_QUERY_MODE, + CHANNEL_QUERY_WHO, + CHANNEL_QUERY_BMODE, + + CHANNEL_QUERIES +}; + +#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO) + #define STRUCT_SERVER_REC IRC_SERVER_REC struct _IRC_CHANNEL_REC { #include @@ -22,6 +32,14 @@ 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_init(void); void irc_channels_deinit(void); diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c index 4965e1bf..76543ac1 100644 --- a/src/irc/core/irc-nicklist.c +++ b/src/irc/core/irc-nicklist.c @@ -293,7 +293,7 @@ static void event_whox_743(SERVER_REC *server, const char *data) g_free(params); } -static void event_whox_745(SERVER_REC *server, const char *data) +static void event_whox_745(IRC_SERVER_REC *server, const char *data) { char *params, *id, *nick, *account; GSList *nicks, *tmp; @@ -306,6 +306,7 @@ static void event_whox_745(SERVER_REC *server, const char *data) g_free(params); return; } + g_hash_table_remove(server->chanqueries->accountqueries, nick); if (strcmp(account, "0") == 0) { account = "*"; @@ -503,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 */ + g_hash_table_remove(server->chanqueries->accountqueries, orignick); + nicklist_rename(SERVER(server), orignick, nick); g_free(params); } 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 98802f7f..b83bdabf 100644 --- a/src/irc/core/massjoin.c +++ b/src/irc/core/massjoin.c @@ -222,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 */ + g_hash_table_remove(server->chanqueries->accountqueries, 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 066ba82f..a4b7b64f 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);