diff --git a/degesch.c b/degesch.c index 3c560be..acd4856 100644 --- a/degesch.c +++ b/degesch.c @@ -4357,27 +4357,6 @@ irc_handle_mode (struct server *s, const struct irc_message *msg) refresh_prompt (s->ctx); } -static struct buffer * -irc_handle_buffer_collision (struct server *s, - struct buffer *buffer, const char *new_name) -{ - struct buffer *collision = str_map_find (&s->irc_buffer_map, new_name); - if (!collision) - return buffer; - - // TODO: use full weechat-style buffer names - // to prevent name collisions with the global buffer - hard_assert (collision->type == buffer->type); - - // When there's a collision, there's not much else we can do - // other than somehow try to merge them - buffer_merge (s->ctx, collision, buffer); - if (s->ctx->current_buffer == buffer) - buffer_activate (s->ctx, collision); - buffer_remove (s->ctx, buffer); - return collision; -} - static void irc_handle_nick (struct server *s, const struct irc_message *msg) { @@ -4392,17 +4371,16 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) if (!user) return; - bool lexicographically_identical = - !irc_server_strcmp (s, user->nickname, new_nickname); + bool lexicographically_different = + !!irc_server_strcmp (s, user->nickname, new_nickname); // What the fuck, someone renamed themselves to ourselves // TODO: probably log a message and force a reconnect - if (!lexicographically_identical + if (lexicographically_different && !irc_server_strcmp (s, new_nickname, s->irc_user->nickname)) return; - // Log a message in any PM buffer and rename it; - // we may even have one for ourselves + // Log a message in any PM buffer (we may even have one for ourselves) struct buffer *pm_buffer = str_map_find (&s->irc_buffer_map, user->nickname); if (pm_buffer) @@ -4413,21 +4391,45 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) buffer_send (s->ctx, pm_buffer, BUFFER_LINE_NICK, 0, .who = who, .object = irc_to_utf8 (s->ctx, new_nickname)); + } - if (!lexicographically_identical) - { - // XXX: this code seems a bit ugly (but also necessary) - user = user_ref (user); + // The new nickname may collide with a user referenced by a PM buffer, + // or in case of data inconsistency with the server, channels. + // In the latter case we need the colliding user to leave all of them. + struct user *user_collision = NULL; + if (lexicographically_different + && (user_collision = str_map_find (&s->irc_users, new_nickname))) + LIST_FOR_EACH (struct user_channel, iter, user_collision->channels) + irc_remove_user_from_channel (user_collision, iter->channel); - pm_buffer = irc_handle_buffer_collision - (s, pm_buffer, new_nickname); + struct buffer *buffer_collision = NULL; + if (lexicographically_different + && (buffer_collision = str_map_find (&s->irc_buffer_map, new_nickname))) + { + hard_assert (buffer_collision->type == BUFFER_PM); + hard_assert (buffer_collision->user == user_collision); - user_unref (pm_buffer->user); - pm_buffer->user = user; + user_unref (buffer_collision->user); + buffer_collision->user = user_ref (user); - str_map_set (&s->irc_buffer_map, user->nickname, NULL); - str_map_set (&s->irc_buffer_map, new_nickname, pm_buffer); - } + // There's not much else we can do other than somehow try to merge + // one buffer into the other. In our case, the original buffer wins. + buffer_merge (s->ctx, buffer_collision, pm_buffer); + if (s->ctx->current_buffer == pm_buffer) + buffer_activate (s->ctx, buffer_collision); + buffer_remove (s->ctx, pm_buffer); + pm_buffer = buffer_collision; + } + + // The colliding user should be completely destroyed by now + if (lexicographically_different) + hard_assert (!str_map_find (&s->irc_users, new_nickname)); + + // Now we can rename the PM buffer to reflect the new nickname + if (pm_buffer) + { + str_map_set (&s->irc_buffer_map, user->nickname, NULL); + str_map_set (&s->irc_buffer_map, new_nickname, pm_buffer); char *x = xstrdup_printf ("%s.%s", s->name, new_nickname); buffer_rename (s->ctx, pm_buffer, x); @@ -4464,13 +4466,9 @@ irc_handle_nick (struct server *s, const struct irc_message *msg) } } - // Finally rename the user - if (!lexicographically_identical) - { - // NOTE: this doesn't dereference anything - str_map_set (&s->irc_users, user->nickname, NULL); - str_map_set (&s->irc_users, new_nickname, user); - } + // Finally rename the user as it should be safe now + str_map_set (&s->irc_users, user->nickname, NULL); + str_map_set (&s->irc_users, new_nickname, user); free (user->nickname); user->nickname = xstrdup (new_nickname);