private void processConfigurationForm(@Nonnull final DataForm completedForm, @Nonnull final Affiliation actorAffiliation, @Nullable final JID actorJid)
        throws ForbiddenException, ConflictException, NotAcceptableException, NotAllowedException
    {
        List<String> values;
        FormField field;

        // Get the new list of admins
        field = completedForm.getField("muc#roomconfig_roomadmins");
        boolean adminsSent = field != null;
        List<JID> admins = new ArrayList<>();
        if (field != null) {
            for (String value : field.getValues()) {
                // XEP-0045: "Affiliations are granted, revoked, and 
                // maintained based on the user's bare JID, (...)"
                if (value != null && !value.trim().isEmpty()) {
                    // could be a group jid
                    admins.add(GroupJID.fromString((value.trim())).asBareJID());
                }
            }
        }

        // Get the new list of owners
        field = completedForm.getField("muc#roomconfig_roomowners");
        boolean ownersSent = field != null;
        List<JID> owners = new ArrayList<>();
        if (field != null) {
            for(String value : field.getValues()) {
                // XEP-0045: "Affiliations are granted, revoked, and 
                // maintained based on the user's bare JID, (...)"
                if (value != null && !value.trim().isEmpty()) {
                    // could be a group jid
                    owners.add(GroupJID.fromString((value.trim())).asBareJID());
                }
            }
        }

        // Answer a conflict error if all the current owners will be removed
        if (ownersSent && owners.isEmpty()) {
            throw new ConflictException();
        }

        // Status codes signifying privacy-related configuration changes to be sent to everyone.
        Set<Integer> statusCodes = new HashSet<>();

        // Keep a registry of the updated presences
        List<Presence> presences = new ArrayList<>(admins.size() + owners.size());

        field = completedForm.getField("muc#roomconfig_roomname");
        if (field != null) {
            final String value = field.getFirstValue();
            room.setNaturalLanguageName((value != null ? value : " "));
        }

        field = completedForm.getField("muc#roomconfig_roomdesc");
        if (field != null) {
            final String value = field.getFirstValue();
            room.setDescription((value != null ? value : " "));
        }

        field = completedForm.getField("muc#roomconfig_changesubject");
        if (field != null) {
            room.setCanOccupantsChangeSubject( parseFirstValueAsBoolean( field, true ) );
        }

        field = completedForm.getField("muc#roomconfig_maxusers");
        if (field != null) {
            final String value = field.getFirstValue();
            room.setMaxUsers((value != null ? Integer.parseInt(value) : 30));
        }

        field = completedForm.getField("muc#roomconfig_presencebroadcast");
        if (field != null) {
            values = new ArrayList<>(field.getValues());
            room.setRolesToBroadcastPresence(values.stream().map(Role::valueOf).collect(Collectors.toList()));
        }

        field = completedForm.getField("muc#roomconfig_publicroom");
        if (field != null) {
            room.setPublicRoom( parseFirstValueAsBoolean( field, true ) );
        }

        field = completedForm.getField("{http://igniterealtime.org}muc#roomconfig_retireondel");
        if (field != null) {
            final boolean newValue = parseFirstValueAsBoolean(field, false);
            room.setRetireOnDeletion(newValue);
        }

        field = completedForm.getField("{http://igniterealtime.org}muc#roomconfig_preservehistondel");
        if (field != null) {
            final boolean newValue = parseFirstValueAsBoolean(field, true);
            room.setPreserveHistOnRoomDeletionEnabled(newValue);
        }

        field = completedForm.getField("muc#roomconfig_persistentroom");
        if (field != null) {
            boolean isPersistent = parseFirstValueAsBoolean( field, true );
            // Delete the room from the DB if it's no longer persistent
            if (room.isPersistent() && !isPersistent) {
                MUCPersistenceManager.deleteFromDB(room);
            }
            room.setPersistent(isPersistent);
        }

        field = completedForm.getField("muc#roomconfig_moderatedroom");
        if (field != null) {
            room.setModerated( parseFirstValueAsBoolean( field, true ) );
        }

        field = completedForm.getField("muc#roomconfig_membersonly");
        if (field != null) {
            presences.addAll(room.setMembersOnly( parseFirstValueAsBoolean( field, true ), actorAffiliation, actorJid ) );
        }

        field = completedForm.getField("muc#roomconfig_allowinvites");
        if (field != null) {
            room.setCanOccupantsInvite( parseFirstValueAsBoolean( field, true ) );
        }


        boolean passwordProtectionChanged = false;
        boolean passwordChanged = false;

        boolean updatedIsPasswordProtected = false;
        String updatedPassword = null;

        field = completedForm.getField("muc#roomconfig_passwordprotectedroom");
        if (field != null)
        {
            passwordProtectionChanged = true;
            updatedIsPasswordProtected = parseFirstValueAsBoolean( field, true );
        }

        field = completedForm.getField("muc#roomconfig_roomsecret");
        if (field != null) {
            passwordChanged = true;
            updatedPassword = completedForm.getField("muc#roomconfig_roomsecret").getFirstValue();
            if ( updatedPassword != null && updatedPassword.isEmpty() )
            {
                updatedPassword = null;
            }
        }

        if ( passwordProtectionChanged )
        {
            // The owner signifies that a change in password-protection status is desired.
            if ( !updatedIsPasswordProtected )
            {
                // The owner lifts password protection.
                room.setPassword( null );
            }
            else if ( updatedPassword == null && room.getPassword() == null )
            {
                // The owner sets password-protection, but does not provide a password (and the room does not already have a password).
                throw new NotAcceptableException( "Room is made password-protected, but is missing a password." );
            }
            else if ( updatedPassword != null )
            {
                // The owner sets password-protection and provided a new password.
                room.setPassword( updatedPassword );
            }
        }
        else if ( passwordChanged )
        {
            // The owner did not explicitly signal a password protection change, but did change the password value.
            // This implies a change in password protection.
            room.setPassword( updatedPassword );
        }

        field = completedForm.getField("muc#roomconfig_whois");
        if (field != null) {
            final boolean newValue = ("anyone".equals(field.getFirstValue()));
            final boolean oldValue = room.canAnyoneDiscoverJID();
            room.setCanAnyoneDiscoverJID(newValue);

            // XEP-0045, section 10.2.1: If the room is now non-anonymous, status code 172.
            if (newValue && !oldValue) {
                statusCodes.add(172);
            }

            // XEP-0045, section 10.2.1: If the room is now semi-anonymous, status code 173.
            if (!newValue && oldValue) {
                statusCodes.add(173);
            }
        }

        field = completedForm.getField("muc#roomconfig_allowpm");
        if (field != null) {
            room.setCanSendPrivateMessage(field.getFirstValue());
        }

        field = completedForm.getField("muc#roomconfig_enablelogging");
        if (field != null) {
            final boolean newValue = parseFirstValueAsBoolean(field, true);
            final boolean oldValue = room.isLogEnabled();
            room.setLogEnabled(newValue);

            // XEP-0045, section 10.2.1: If room logging is now enabled, status code 170.
            if (newValue && !oldValue) {
                statusCodes.add(170);
            }

            // XEP-0045, section 10.2.1: If room logging is now disabled, status code 171.
            if (!newValue && oldValue) {
                statusCodes.add(171);
            }
        }

        field = completedForm.getField("x-muc#roomconfig_reservednick");
        if (field != null) {
            room.setLoginRestrictedToNickname( parseFirstValueAsBoolean( field, true ) );
        }

        field = completedForm.getField("x-muc#roomconfig_canchangenick");
        if (field != null) {
            room.setChangeNickname( parseFirstValueAsBoolean( field, true ) );
        }

        field = completedForm.getField("x-muc#roomconfig_registration");
        if (field != null) {
            room.setRegistrationEnabled( parseFirstValueAsBoolean( field, true ) );
        }

        // Update the modification date to reflect the last time when the room's configuration
        // was modified
        room.setModificationDate(new Date());

        if (room.isPersistent()) {
            room.saveToDB();
        }

        // Apply any configuration changes to the FMUC state.
        room.getFmucHandler().applyConfigurationChanges();

        // Set the new owners and admins of the room
        presences.addAll(room.addOwners(owners, actorAffiliation));
        presences.addAll(room.addAdmins(admins, actorAffiliation));

        if (ownersSent) {
            // Change the affiliation to "member" for the current owners that won't be neither
            // owner nor admin (if the form included the owners field)
            List<JID> ownersToRemove = new ArrayList<>(room.getOwners());
            ownersToRemove.removeAll(admins);
            ownersToRemove.removeAll(owners);
            for (JID jid : ownersToRemove) {
                // ignore group jids
                if (!GroupJID.isGroup(jid)) {
                    presences.addAll(room.addMember(jid, null, actorAffiliation));
                }
            }
        }

        if (adminsSent) {
            // Change the affiliation to "member" for the current admins that won't be neither
            // owner nor admin (if the form included the admins field)
            List<JID> adminsToRemove = new ArrayList<>(room.getAdmins());
            adminsToRemove.removeAll(admins);
            adminsToRemove.removeAll(owners);
            for (JID jid : adminsToRemove) {
                // ignore group jids
                if (!GroupJID.isGroup(jid)) {
                    presences.addAll(room.addMember(jid, null, actorAffiliation));
                }
            }
        }

        // Destroy the room if the room is no longer persistent and there are no occupants in
        // the room
        if (!room.isPersistent() && room.getOccupantsCount() == 0) {
            room.destroyRoom(null, null);
        }

        // Send the updated presences to the room occupants
        for (Presence presence : presences) {
            room.send(presence, room.getSelfRepresentation());
        }

        // XEP-0045 section 10.2.1 "Notification of Configuration Changes: A room MUST send notification to all occupants
        // when the room configuration changes in a way that has an impact on the privacy or security profile of the room.
        if (!statusCodes.isEmpty()) {
            final Message message = new Message();
            message.setFrom(room.getJID());
            message.setTo(room.getJID());
            message.setType(Message.Type.groupchat);
            final Element x = message.addChildElement("x", "http://jabber.org/protocol/muc#user");
            statusCodes.forEach(code -> x.addElement("status").addAttribute("code", String.valueOf(code)));

            room.send(message, room.getSelfRepresentation());
        }
    }
Processes the completed form sent by an owner of the room. This will modify the room's
configuration as well as the list of owners and admins.

@param completedForm the completed form sent by an owner of the room.
@param actorAffiliation the room affiliation of the user that sent the completed form.
@param actorJid the (real) user address of the user that sent the completed form.
@throws ForbiddenException if the user does not have enough privileges.
@throws ConflictException If the room was going to lose all of its owners.
@throws NotAcceptableException if the room requires a password that was not supplied
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java