private MUCRoom processRoomJoinRequest(
        @Nonnull final Presence packet,
        @Nonnull final String roomName,
        @Nullable MUCRoom room,
        @Nullable String nickname )
    {
        Log.trace("Processing join request from '{}' for room '{}'", packet.getFrom(), roomName);

        if ( nickname == null )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: request did not specify a nickname", packet.getFrom(), roomName);

            // A resource is required in order to join a room http://xmpp.org/extensions/xep-0045.html#enter
            // If the user does not specify a room nickname (note the bare JID on the 'from' address in the following example), the service MUST return a <jid-malformed/> error
            if ( packet.getType() != Presence.Type.error )
            {
                sendErrorPacket(packet, PacketError.Condition.jid_malformed, "A nickname (resource-part) is required in order to join a room.");
            }
            return null;
        }

        if ( !packet.isAvailable() )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: request unexpectedly provided a presence stanza of type '{}'. Expected none.", packet.getFrom(), roomName, packet.getType());
            if ( packet.getType() != Presence.Type.error )
            {
                sendErrorPacket(packet, PacketError.Condition.unexpected_request, "Unexpected stanza type: " + packet.getType());
            }
            return null;
        }

        if (room == null) {
            try {
                // Create the room
                room = getChatRoom(roomName, packet.getFrom());
            } catch (NotAllowedException e) {
                String errorMessage = switch (e.getReason()) {
                    case ROOM_RETIRED -> "This room name cannot be used as it has been retired.";
                    case INSUFFICIENT_PERMISSIONS -> "You do not have permission to create a new room.";
                };

                Log.debug("Request from '{}' to join room '{}' rejected: {}", packet.getFrom(), roomName, errorMessage, e);

                sendErrorPacket(packet, PacketError.Condition.not_allowed, errorMessage);
                return null;
            }
        }

        try
        {
            // User must support MUC in order to create a room
            HistoryRequest historyRequest = null;
            String password = null;

            // Check for password & requested history if client supports MUC
            final Element mucInfo = packet.getChildElement("x", "http://jabber.org/protocol/muc");
            if ( mucInfo != null )
            {
                password = mucInfo.elementTextTrim("password");
                if ( mucInfo.element("history") != null )
                {
                    historyRequest = new HistoryRequest(mucInfo);
                }
            }

            // The user joins the room
            final MUCOccupant occupantData = room.joinRoom(nickname,
                password,
                historyRequest,
                packet.getFrom(),
                packet.createCopy());

            // If the client that created the room is non-MUC compliant then
            // unlock the room thus creating an "instant" room
            if ( mucInfo == null && room.isLocked() && !room.isManuallyLocked() )
            {
                room.unlock(occupantData.getAffiliation());
            }
        }
        catch ( UnauthorizedException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: user not authorized to create or join the room.", packet.getFrom(), roomName, e);
            sendErrorPacket(packet, PacketError.Condition.not_authorized, "You're not authorized to create or join the room.");
        }
        catch ( ServiceUnavailableException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: the maximum number of users of the room has been reached.", packet.getFrom(), roomName, e);
            sendErrorPacket(packet, PacketError.Condition.service_unavailable, "The maximum number of users of the room has been reached.");
        }
        catch ( UserAlreadyExistsException | ConflictException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: the requested nickname '{}' is being used by someone else in the room.", packet.getFrom(), roomName, nickname, e);
            sendErrorPacket(packet, PacketError.Condition.conflict, "The nickname that is being used is used by someone else.");
        }
        catch ( RoomLockedException e )
        {
            // If a user attempts to enter a room while it is "locked" (i.e., before the room creator provides an initial configuration and therefore before the room officially exists), the service MUST refuse entry and return an <item-not-found/> error to the user
            Log.debug("Request from '{}' to join room '{}' rejected: room is locked.", packet.getFrom(), roomName, e);
            sendErrorPacket(packet, PacketError.Condition.item_not_found, "This room is locked (it might not have been configured yet).");
        }
        catch ( ForbiddenException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: user not authorized join the room.", packet.getFrom(), roomName, e);
            sendErrorPacket(packet, PacketError.Condition.forbidden, "You're not allowed to join this room.");
        }
        catch ( RegistrationRequiredException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: room is member-only, user is not a member.", packet.getFrom(), roomName, e);
            sendErrorPacket(packet, PacketError.Condition.registration_required, "This is a member-only room. Membership is required.");
        }
        catch ( NotAcceptableException e )
        {
            Log.debug("Request from '{}' to join room '{}' rejected: user attempts to use nickname '{}' which is different from the reserved nickname.", packet.getFrom(), roomName, nickname, e);
            sendErrorPacket(packet, PacketError.Condition.not_acceptable, "You're trying to join with a nickname different than the reserved nickname.");
        }
        return room;
    }
Process a request to join a room.

This method might be invoked for a room that does not yet exist (when the presence is a room-creation request).

@param packet   The stanza representing the nickname-change request.
@param roomName The name of the room that the stanza was addressed to.
@param room     The room that the stanza was addressed to, if it exists.
@param nickname The requested nickname.
@return the room that handled the request
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java