MucConfigFormManager.java

  1. /**
  2.  *
  3.  * Copyright 2015-2024 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.muc;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.List;

  21. import org.jivesoftware.smack.SmackException.NoResponseException;
  22. import org.jivesoftware.smack.SmackException.NotConnectedException;
  23. import org.jivesoftware.smack.XMPPException.XMPPErrorException;

  24. import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException;
  25. import org.jivesoftware.smackx.xdata.BooleanFormField;
  26. import org.jivesoftware.smackx.xdata.FormField;
  27. import org.jivesoftware.smackx.xdata.form.FillableForm;
  28. import org.jivesoftware.smackx.xdata.form.FilledForm;
  29. import org.jivesoftware.smackx.xdata.form.Form;

  30. import org.jxmpp.jid.Jid;
  31. import org.jxmpp.jid.util.JidUtil;

  32. /**
  33.  * Multi-User Chat configuration form manager is used to fill out and submit a {@link FilledForm} used to
  34.  * configure rooms.
  35.  * <p>
  36.  * Room configuration needs either be done right after the room is created and still locked. Or at
  37.  * any later point (see <a href="http://xmpp.org/extensions/xep-0045.html#roomconfig">XEP-45 § 10.2
  38.  * Subsequent Room Configuration</a>). When done with the configuration, call
  39.  * {@link #submitConfigurationForm()}.
  40.  * </p>
  41.  * <p>
  42.  * The manager may not provide all possible configuration options. If you want direct access to the
  43.  * configuration form, use {@link MultiUserChat#getConfigurationForm()} and
  44.  * {@link MultiUserChat#sendConfigurationForm(FillableForm)}.
  45.  * </p>
  46.  */
  47. public class MucConfigFormManager {

  48.     private static final String HASH_ROOMCONFIG = "#roomconfig";

  49.     public static final String FORM_TYPE = MultiUserChatConstants.NAMESPACE + HASH_ROOMCONFIG;

  50.                     /**
  51.      * The constant String {@value}.
  52.      *
  53.      * @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a>
  54.      */
  55.     public static final String MUC_ROOMCONFIG_ROOMOWNERS = "muc#roomconfig_roomowners";

  56.     /**
  57.      * The constant String {@value}.
  58.      *
  59.      * @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a>
  60.      */
  61.     public static final String MUC_ROOMCONFIG_ROOMADMINS = "muc#roomconfig_roomadmins";

  62.     /**
  63.      * The constant String {@value}.
  64.      */
  65.     public static final String MUC_ROOMCONFIG_MEMBERSONLY = "muc#roomconfig_membersonly";

  66.     /**
  67.      * The constant String {@value}.
  68.      *
  69.      * @see <a href="http://xmpp.org/extensions/xep-0045.html#enter-pw">XEP-0045 § 7.2.6 Password-Protected Rooms</a>
  70.      */
  71.     public static final String MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM = "muc#roomconfig_passwordprotectedroom";

  72.     /**
  73.      * The constant String {@value}.
  74.      */
  75.     public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";

  76.     /**
  77.      * The constant String {@value}.
  78.      */
  79.     public static final String MUC_ROOMCONFIG_MODERATEDROOM = "muc#roomconfig_moderatedroom";

  80.     /**
  81.      * The constant String {@value}.
  82.      */
  83.     public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom";

  84.     /**
  85.      * The constant String {@value}.
  86.      */
  87.     public static final String MUC_ROOMCONFIG_ROOMNAME = "muc#roomconfig_roomname";

  88.     /**
  89.      * The constant String {@value}.
  90.      */
  91.     public static final String MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING = "muc#roomconfig_enablelogging";

  92.     /**
  93.      * The constant String {@value}.
  94.      */
  95.     public static final String MUC_ROOMCONFIG_CHANGE_SUBJECT = "muc#roomconfig_changesubject";

  96.     private final MultiUserChat multiUserChat;
  97.     private final FillableForm answerForm;
  98.     private final List<Jid> owners;
  99.     private final List<Jid> admins;

  100.     /**
  101.      * Create a new MUC config form manager.
  102.      * <p>
  103.      * Note that the answerForm needs to be filled out with the defaults.
  104.      * </p>
  105.      *
  106.      * @param multiUserChat the MUC for this configuration form.
  107.      * @throws InterruptedException if the calling thread was interrupted.
  108.      * @throws NotConnectedException if the XMPP connection is not connected.
  109.      * @throws XMPPErrorException if there was an XMPP error returned.
  110.      * @throws NoResponseException if there was no response from the remote entity.
  111.      */
  112.     MucConfigFormManager(MultiUserChat multiUserChat) throws NoResponseException,
  113.                     XMPPErrorException, NotConnectedException, InterruptedException {
  114.         this.multiUserChat = multiUserChat;

  115.         // Set the answer form
  116.         Form configForm = multiUserChat.getConfigurationForm();
  117.         this.answerForm = configForm.getFillableForm();

  118.         // Set the local variables according to the fields found in the answer form
  119.         FormField roomOwnersFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMOWNERS);
  120.         if (roomOwnersFormField != null) {
  121.             // Set 'owners' to the currently configured owners
  122.             List<? extends CharSequence> ownerStrings = roomOwnersFormField.getValues();
  123.             owners = new ArrayList<>(ownerStrings.size());
  124.             JidUtil.jidsFrom(ownerStrings, owners, null);
  125.         }
  126.         else {
  127.             // roomowners not supported, this should barely be the case
  128.             owners = null;
  129.         }

  130.         FormField roomAdminsFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMADMINS);
  131.         if (roomAdminsFormField != null) {
  132.             // Set 'admins' to the currently configured admins
  133.             List<? extends CharSequence> adminStrings = roomAdminsFormField.getValues();
  134.             admins = new ArrayList<>(adminStrings.size());
  135.             JidUtil.jidsFrom(adminStrings, admins, null);
  136.         }
  137.         else {
  138.             // roomadmins not supported, this should barely be the case
  139.             admins = null;
  140.         }
  141.     }

  142.     /**
  143.      * Check if the room supports room owners.
  144.      * @return <code>true</code> if supported, <code>false</code> if not.
  145.      * @see #MUC_ROOMCONFIG_ROOMOWNERS
  146.      */
  147.     public boolean supportsRoomOwners() {
  148.         return owners != null;
  149.     }

  150.     /**
  151.      * Check if the room supports room admins.
  152.      * @return <code>true</code> if supported, <code>false</code> if not.
  153.      * @see #MUC_ROOMCONFIG_ROOMADMINS
  154.      */
  155.     public boolean supportsRoomAdmins() {
  156.         return admins != null;
  157.     }

  158.     /**
  159.      * Set the owners of the room.
  160.      *
  161.      * @param newOwners a collection of JIDs to become the new owners of the room.
  162.      * @return a reference to this object.
  163.      * @throws MucConfigurationNotSupportedException if the MUC service does not support this option.
  164.      * @see #MUC_ROOMCONFIG_ROOMOWNERS
  165.      */
  166.     public MucConfigFormManager setRoomOwners(Collection<? extends Jid> newOwners) throws MucConfigurationNotSupportedException {
  167.         if (!supportsRoomOwners()) {
  168.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMOWNERS);
  169.         }
  170.         owners.clear();
  171.         owners.addAll(newOwners);
  172.         return this;
  173.     }

  174.     /**
  175.      * Set the admins of the room.
  176.      *
  177.      * @param newAdmins a collection of JIDs to become the new admins of the room.
  178.      * @return a reference to this object.
  179.      * @throws MucConfigurationNotSupportedException if the MUC service does not support this option.
  180.      * @see #MUC_ROOMCONFIG_ROOMADMINS
  181.      */
  182.     public MucConfigFormManager setRoomAdmins(Collection<? extends Jid> newAdmins) throws MucConfigurationNotSupportedException {
  183.         if (!supportsRoomAdmins()) {
  184.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMADMINS);
  185.         }
  186.         admins.clear();
  187.         admins.addAll(newAdmins);
  188.         return this;
  189.     }

  190.     /**
  191.      * Check if the room supports a members only configuration.
  192.      *
  193.      * @return <code>true</code> if supported, <code>false</code> if not.
  194.      */
  195.     public boolean supportsMembersOnly() {
  196.         return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY);
  197.     }

  198.     /**
  199.      * Check if the room supports being moderated in the configuration.
  200.      *
  201.      * @return <code>true</code> if supported, <code>false</code> if not.
  202.      */
  203.     public boolean supportsModeration() {
  204.         return answerForm.hasField(MUC_ROOMCONFIG_MODERATEDROOM);
  205.     }

  206.     /**
  207.      * Make the room for members only.
  208.      *
  209.      * @return a reference to this object.
  210.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  211.      */
  212.     public MucConfigFormManager makeMembersOnly() throws MucConfigurationNotSupportedException {
  213.         return setMembersOnly(true);
  214.     }

  215.     /**
  216.      * Set if the room is members only. Rooms are not members only per default.
  217.      *
  218.      * @param isMembersOnly if the room should be members only.
  219.      * @return a reference to this object.
  220.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  221.      */
  222.     public MucConfigFormManager setMembersOnly(boolean isMembersOnly) throws MucConfigurationNotSupportedException {
  223.         if (!supportsMembersOnly()) {
  224.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MEMBERSONLY);
  225.         }
  226.         answerForm.setAnswer(MUC_ROOMCONFIG_MEMBERSONLY, isMembersOnly);
  227.         return this;
  228.     }


  229.     /**
  230.      * Make the room moderated.
  231.      *
  232.      * @return a reference to this object.
  233.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  234.      */
  235.     public MucConfigFormManager makeModerated() throws MucConfigurationNotSupportedException {
  236.         return setModerated(true);
  237.     }

  238.     /**
  239.      * Set if the room is members only. Rooms are not members only per default.
  240.      *
  241.      * @param isModerated if the room should be moderated.
  242.      * @return a reference to this object.
  243.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  244.      */
  245.     public MucConfigFormManager setModerated(boolean isModerated) throws MucConfigurationNotSupportedException {
  246.         if (!supportsModeration()) {
  247.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MODERATEDROOM);
  248.         }
  249.         answerForm.setAnswer(MUC_ROOMCONFIG_MODERATEDROOM, isModerated);
  250.         return this;
  251.     }


  252.     /**
  253.      * Check if the room supports its visibility being controlled via configuration.
  254.      *
  255.      * @return <code>true</code> if supported, <code>false</code> if not.
  256.      */
  257.     public boolean supportsPublicRoom() {
  258.         return answerForm.hasField(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM);
  259.     }

  260.     /**
  261.      * Make the room publicly searchable.
  262.      *
  263.      * @return a reference to this object.
  264.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  265.      */
  266.     public MucConfigFormManager makePublic() throws MucConfigurationNotSupportedException {
  267.         return setPublic(true);
  268.     }

  269.     /**
  270.      * Make the room hidden (not publicly searchable).
  271.      *
  272.      * @return a reference to this object.
  273.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  274.      */
  275.     public MucConfigFormManager makeHidden() throws MucConfigurationNotSupportedException {
  276.         return setPublic(false);
  277.     }

  278.     /**
  279.      * Set if the room is publicly searchable (i.e. visible via discovery requests to the MUC service).
  280.      *
  281.      * @param isPublic if the room should be publicly searchable.
  282.      * @return a reference to this object.
  283.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  284.      */
  285.     public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException {
  286.         if (!supportsPublicRoom()) {
  287.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM);
  288.         }
  289.         answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic);
  290.         return this;
  291.     }

  292.     public boolean supportsRoomname() {
  293.         return answerForm.hasField(MUC_ROOMCONFIG_ROOMNAME);
  294.     }

  295.     public MucConfigFormManager setRoomName(String roomName) throws MucConfigurationNotSupportedException {
  296.         if (!supportsRoomname()) {
  297.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMNAME);
  298.         }
  299.         answerForm.setAnswer(MUC_ROOMCONFIG_ROOMNAME, roomName);
  300.         return this;
  301.     }

  302.     /**
  303.      * Check if the room supports password protection.
  304.      *
  305.      * @return <code>true</code> if supported, <code>false</code> if not.
  306.      */
  307.     public boolean supportsPasswordProtected() {
  308.         return answerForm.hasField(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
  309.     }

  310.     /**
  311.      * Set a password and make the room password protected. Users will need to supply the password
  312.      * to join the room.
  313.      *
  314.      * @param password the password to set.
  315.      * @return a reference to this object.
  316.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  317.      */
  318.     public MucConfigFormManager setAndEnablePassword(String password)
  319.                     throws MucConfigurationNotSupportedException {
  320.         return setIsPasswordProtected(true).setRoomSecret(password);
  321.     }

  322.     /**
  323.      * Make the room password protected.
  324.      *
  325.      * @return a reference to this object.
  326.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  327.      */
  328.     public MucConfigFormManager makePasswordProtected() throws MucConfigurationNotSupportedException {
  329.         return setIsPasswordProtected(true);
  330.     }

  331.     /**
  332.      * Set if this room is password protected. Rooms are by default not password protected.
  333.      *
  334.      * @param isPasswordProtected TODO javadoc me please
  335.      * @return a reference to this object.
  336.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  337.      */
  338.     public MucConfigFormManager setIsPasswordProtected(boolean isPasswordProtected)
  339.                     throws MucConfigurationNotSupportedException {
  340.         if (!supportsPasswordProtected()) {
  341.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
  342.         }
  343.         answerForm.setAnswer(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM, isPasswordProtected);
  344.         return this;
  345.     }

  346.     public boolean supportsPublicLogging() {
  347.         return answerForm.hasField(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING);
  348.     }

  349.     public MucConfigFormManager setPublicLogging(boolean enabled) throws MucConfigurationNotSupportedException {
  350.         if (!supportsPublicLogging()) {
  351.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING);
  352.         }
  353.         answerForm.setAnswer(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING, enabled);
  354.         return this;
  355.     }

  356.     public MucConfigFormManager enablePublicLogging() throws MucConfigurationNotSupportedException {
  357.         return setPublicLogging(true);
  358.     }

  359.     public MucConfigFormManager disablPublicLogging() throws MucConfigurationNotSupportedException {
  360.         return setPublicLogging(false);
  361.     }

  362.     /**
  363.      * Set the room secret, aka the room password. If set and enabled, the password is required to
  364.      * join the room. Note that this does only set it by does not enable password protection. Use
  365.      * {@link #setAndEnablePassword(String)} to set a password and make the room protected.
  366.      *
  367.      * @param secret the secret/password.
  368.      * @return a reference to this object.
  369.      * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
  370.      */
  371.     public MucConfigFormManager setRoomSecret(String secret)
  372.                     throws MucConfigurationNotSupportedException {
  373.         if (!answerForm.hasField(MUC_ROOMCONFIG_ROOMSECRET)) {
  374.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMSECRET);
  375.         }
  376.         answerForm.setAnswer(MUC_ROOMCONFIG_ROOMSECRET, secret);
  377.         return this;
  378.     }

  379.     public boolean supportsChangeSubjectByOccupant() {
  380.         return answerForm.hasField(MUC_ROOMCONFIG_CHANGE_SUBJECT);
  381.     }

  382.     public boolean occupantsAreAllowedToChangeSubject() throws MucConfigurationNotSupportedException {
  383.         if (!supportsChangeSubjectByOccupant()) {
  384.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT);
  385.         }
  386.         return answerForm.getField(MUC_ROOMCONFIG_CHANGE_SUBJECT).ifPossibleAsOrThrow(BooleanFormField.class).getValueAsBoolean();
  387.     }

  388.     public MucConfigFormManager setChangeSubjectByOccupant(boolean enabled) throws MucConfigurationNotSupportedException {
  389.         if (!supportsChangeSubjectByOccupant()) {
  390.             throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT);
  391.         }
  392.         answerForm.setAnswer(MUC_ROOMCONFIG_CHANGE_SUBJECT, enabled);
  393.         return this;
  394.     }

  395.     public MucConfigFormManager allowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException {
  396.         return setChangeSubjectByOccupant(true);
  397.     }

  398.     public MucConfigFormManager disallowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException {
  399.         return setChangeSubjectByOccupant(false);
  400.     }

  401.     /**
  402.      * Submit the configuration as {@link FilledForm} to the room.
  403.      *
  404.      * @throws NoResponseException if there was no response from the room.
  405.      * @throws XMPPErrorException if there was an XMPP error returned.
  406.      * @throws NotConnectedException if the XMPP connection is not connected.
  407.      * @throws InterruptedException if the calling thread was interrupted.
  408.      */
  409.     public void submitConfigurationForm() throws NoResponseException, XMPPErrorException, NotConnectedException,
  410.                     InterruptedException {
  411.         if (owners != null) {
  412.             answerForm.setAnswer(MUC_ROOMCONFIG_ROOMOWNERS, JidUtil.toStringList(owners));
  413.         }
  414.         if (admins != null) {
  415.             answerForm.setAnswer(MUC_ROOMCONFIG_ROOMADMINS, JidUtil.toStringList(admins));
  416.         }
  417.         multiUserChat.sendConfigurationForm(answerForm);
  418.     }
  419. }