diff --git a/degesch.c b/degesch.c index f997471..a1920cb 100644 --- a/degesch.c +++ b/degesch.c @@ -1059,6 +1059,8 @@ struct server // TODO: an output queue to prevent excess floods (this will be needed // especially for away status polling) + bool rehashing; ///< Rehashing IRC identifiers + struct str_map irc_users; ///< IRC user data struct str_map irc_channels; ///< IRC channel data struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers @@ -2791,7 +2793,8 @@ irc_user_on_destroy (void *object, void *user_data) { struct user *user = object; struct server *s = user_data; - str_map_set (&s->irc_users, user->nickname, NULL); + if (!s->rehashing) + str_map_set (&s->irc_users, user->nickname, NULL); } static struct user * @@ -2877,7 +2880,8 @@ irc_channel_on_destroy (void *object, void *user_data) struct server *s = user_data; LIST_FOR_EACH (struct channel_user, iter, channel->users) irc_channel_unlink_user (channel, iter); - str_map_set (&s->irc_channels, channel->name, NULL); + if (!s->rehashing) + str_map_set (&s->irc_channels, channel->name, NULL); } static struct channel * @@ -2924,7 +2928,74 @@ irc_left_channel (struct channel *channel) static void irc_rehash_and_fix_conflicts (struct server *s) { - // TODO + // Save the old maps and initialize new ones + struct str_map old_users = s->irc_users; + struct str_map old_channels = s->irc_channels; + struct str_map old_buffer_map = s->irc_buffer_map; + + str_map_init (&s->irc_users); + str_map_init (&s->irc_channels); + str_map_init (&s->irc_buffer_map); + + s->irc_users .free = old_users .free; + s->irc_channels .free = old_channels .free; + s->irc_buffer_map.free = old_buffer_map.free; + + s->irc_users .key_xfrm = s->irc_strxfrm; + s->irc_channels .key_xfrm = s->irc_strxfrm; + s->irc_buffer_map.key_xfrm = s->irc_strxfrm; + + // Prevent channels and users from unsetting themselves + // from server maps upon removing the last reference to them + s->rehashing = true; + + // TODO: "Removed similarly named buffer %s because of casemapping conflict" + // XXX: to be perfectly sure, we should also check + // whether any users collide with channels and vice versa + + struct str_map_iter iter; + str_map_iter_init (&iter, &old_users); + struct user *user; + while ((user = str_map_iter_next (&iter))) + { + // FIXME: don't remove ourselves! + if (str_map_find (&s->irc_users, user->nickname)) + { + // TODO: move or merge any PM buffer and remove + // the user from channels and altogether + } + else + { + str_map_set (&s->irc_users, user->nickname, user); + str_map_set (&s->irc_buffer_map, user->nickname, + str_map_find (&old_buffer_map, user->nickname)); + } + } + + str_map_iter_init (&iter, &old_channels); + struct channel *channel; + while ((channel = str_map_iter_next (&iter))) + { + if (str_map_find (&s->irc_channels, channel->name)) + { + // TODO: remove all users from the buffer + // and probably issue NAMES if registered and on the channel, + // and of course remove the colliding channel + } + else + { + str_map_set (&s->irc_channels, channel->name, user); + str_map_set (&s->irc_buffer_map, user->nickname, + str_map_find (&old_buffer_map, user->nickname)); + } + } + + // Hopefully we've either moved or destroyed all the old content + s->rehashing = false; + + str_map_free (&old_users); + str_map_free (&old_channels); + str_map_free (&old_buffer_map); } static void