public void processVCardResponse(@Nonnull final IQ response)
    {
        if (!response.isResponse()) {
            throw new IllegalArgumentException("IQ must be a response, but is of type: " + response.getType());
        }

        // Name of the room that the stanza is addressed to.
        final String roomName = response.getTo().getNode();

        if ( roomName == null )
        {
            // Packets to the groupchat service (as opposed to a specific room on the service). This should not occur
            // (should be handled by MultiUserChatServiceImpl instead).
            Log.warn(LocaleUtils.getLocalizedString("muc.error.not-supported") + " " + response);
            return;
        }

        StanzaIDUtil.ensureUniqueAndStableStanzaID(response, response.getTo().asBareJID());

        final Lock lock = getChatRoomLock(roomName);
        lock.lock();
        try {
            // Get the room, if one exists.
            @Nullable MUCRoom room = getChatRoom(roomName);

            // Determine if this sender (responding to the VCard request) is an occupant in the addressed room.
            // VCards responses are sent by servers on behalf of the user, so have a bare JID in the 'from' address.
            // This cannot be uniquely matched to a single occupant. In case that they are with multiple occupants in
            // the room, we will use the role with the most liberal permissions.
            MUCOccupant occupant;
            if (room == null) {
                occupant = null;
            } else {
                try {
                    occupant = room.getOccupantsByBareJID(response.getFrom().asBareJID())
                        .stream()
                        .min(Comparator.comparingInt(o -> o.getRole().getValue()))
                        .orElse(null);
                } catch (UserNotFoundException e) {
                    occupant = null;
                }
            }

            if (occupant != null) {
                Log.debug("Occupant data for user {} in room {}: {}", response.getFrom(), roomName, occupant);
            } else if (room == null) {
                Log.debug("Occupant data for user {} in room {} does not exist, as the room does not exist.", response.getFrom(), roomName);
            } else {
                Log.debug("Occupant data for user {} in room {} does not exist, as the user is currently not in the room.", response.getFrom(), roomName);
            }

            process(response, room, occupant);

            // Ensure that other cluster nodes see any changes (unlikely to have occurred here, but better safe than sorry) that might have been applied.
            if (room != null) {
                syncChatRoom(room);
            }
        } finally {
            lock.unlock();
        }
    }
Processes a VCard response stanza that is supposedly sent to an occupant of a room as a result of that occupant
having requested the VCard of another occupant earlier.

VCard processing in MUC rooms depends on a bit of a hack: most clients of requestees cannot process a request for
a VCard themselves (see XEP-0054). Instead, the request is rerouted to the bare JID of the requestee, which is
responded to by its home server. {@link MUCRoom#sendPrivatePacket(Packet, MUCOccupant)} implements this rerouting.
The response from the home server is then processed by this method. It needs special care as its 'from' address
is a bare JID (and thus not directly related to a single occupant).

@param response An IQ stanza that should be a response to an earlier VCard request.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java