001/**
002 *
003 * Copyright 2015-2020 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.muc;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.List;
022
023import org.jivesoftware.smack.SmackException.NoResponseException;
024import org.jivesoftware.smack.SmackException.NotConnectedException;
025import org.jivesoftware.smack.XMPPException.XMPPErrorException;
026
027import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException;
028import org.jivesoftware.smackx.xdata.FormField;
029import org.jivesoftware.smackx.xdata.form.FillableForm;
030import org.jivesoftware.smackx.xdata.form.FilledForm;
031import org.jivesoftware.smackx.xdata.form.Form;
032
033import org.jxmpp.jid.Jid;
034import org.jxmpp.jid.util.JidUtil;
035
036/**
037 * Multi-User Chat configuration form manager is used to fill out and submit a {@link FilledForm} used to
038 * configure rooms.
039 * <p>
040 * Room configuration needs either be done right after the room is created and still locked. Or at
041 * any later point (see <a href="http://xmpp.org/extensions/xep-0045.html#roomconfig">XEP-45 § 10.2
042 * Subsequent Room Configuration</a>). When done with the configuration, call
043 * {@link #submitConfigurationForm()}.
044 * </p>
045 * <p>
046 * The manager may not provide all possible configuration options. If you want direct access to the
047 * configuration form, use {@link MultiUserChat#getConfigurationForm()} and
048 * {@link MultiUserChat#sendConfigurationForm(FillableForm)}.
049 * </p>
050 */
051public class MucConfigFormManager {
052
053    private static final String HASH_ROOMCONFIG = "#roomconfig";
054
055    public static final String FORM_TYPE = MultiUserChatConstants.NAMESPACE + HASH_ROOMCONFIG;
056
057                    /**
058     * The constant String {@value}.
059     *
060     * @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a>
061     */
062    public static final String MUC_ROOMCONFIG_ROOMOWNERS = "muc#roomconfig_roomowners";
063
064    /**
065     * The constant String {@value}.
066     */
067    public static final String MUC_ROOMCONFIG_MEMBERSONLY = "muc#roomconfig_membersonly";
068
069    /**
070     * The constant String {@value}.
071     *
072     * @see <a href="http://xmpp.org/extensions/xep-0045.html#enter-pw">XEP-0045 § 7.2.6 Password-Protected Rooms</a>
073     */
074    public static final String MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM = "muc#roomconfig_passwordprotectedroom";
075
076    /**
077     * The constant String {@value}.
078     */
079    public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";
080
081    private final MultiUserChat multiUserChat;
082    private final FillableForm answerForm;
083    private final List<Jid> owners;
084
085    /**
086     * Create a new MUC config form manager.
087     * <p>
088     * Note that the answerForm needs to be filled out with the defaults.
089     * </p>
090     *
091     * @param multiUserChat the MUC for this configuration form.
092     * @throws InterruptedException if the calling thread was interrupted.
093     * @throws NotConnectedException if the XMPP connection is not connected.
094     * @throws XMPPErrorException if there was an XMPP error returned.
095     * @throws NoResponseException if there was no response from the remote entity.
096     */
097    MucConfigFormManager(MultiUserChat multiUserChat) throws NoResponseException,
098                    XMPPErrorException, NotConnectedException, InterruptedException {
099        this.multiUserChat = multiUserChat;
100
101        // Set the answer form
102        Form configForm = multiUserChat.getConfigurationForm();
103        this.answerForm = configForm.getFillableForm();
104
105        // Set the local variables according to the fields found in the answer form
106        FormField roomOwnersFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMOWNERS);
107        if (roomOwnersFormField != null) {
108            // Set 'owners' to the currently configured owners
109            List<? extends CharSequence> ownerStrings = roomOwnersFormField.getValues();
110            owners = new ArrayList<>(ownerStrings.size());
111            JidUtil.jidsFrom(ownerStrings, owners, null);
112        }
113        else {
114            // roomowners not supported, this should barely be the case
115            owners = null;
116        }
117    }
118
119    /**
120     * Check if the room supports room owners.
121     * @return <code>true</code> if supported, <code>false</code> if not.
122     * @see #MUC_ROOMCONFIG_ROOMOWNERS
123     */
124    public boolean supportsRoomOwners() {
125        return owners != null;
126    }
127
128    /**
129     * Set the owners of the room.
130     *
131     * @param newOwners a collection of JIDs to become the new owners of the room.
132     * @return a reference to this object.
133     * @throws MucConfigurationNotSupportedException if the MUC service does not support this option.
134     * @see #MUC_ROOMCONFIG_ROOMOWNERS
135     */
136    public MucConfigFormManager setRoomOwners(Collection<? extends Jid> newOwners) throws MucConfigurationNotSupportedException {
137        if (!supportsRoomOwners()) {
138            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMOWNERS);
139        }
140        owners.clear();
141        owners.addAll(newOwners);
142        return this;
143    }
144
145    /**
146     * Check if the room supports a members only configuration.
147     *
148     * @return <code>true</code> if supported, <code>false</code> if not.
149     */
150    public boolean supportsMembersOnly() {
151        return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY);
152    }
153
154    /**
155     * Make the room for members only.
156     *
157     * @return a reference to this object.
158     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
159     */
160    public MucConfigFormManager makeMembersOnly() throws MucConfigurationNotSupportedException {
161        return setMembersOnly(true);
162    }
163
164    /**
165     * Set if the room is members only. Rooms are not members only per default.
166     *
167     * @param isMembersOnly if the room should be members only.
168     * @return a reference to this object.
169     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
170     */
171    public MucConfigFormManager setMembersOnly(boolean isMembersOnly) throws MucConfigurationNotSupportedException {
172        if (!supportsMembersOnly()) {
173            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MEMBERSONLY);
174        }
175        answerForm.setAnswer(MUC_ROOMCONFIG_MEMBERSONLY, isMembersOnly);
176        return this;
177    }
178
179    /**
180     * Check if the room supports password protection.
181     *
182     * @return <code>true</code> if supported, <code>false</code> if not.
183     */
184    public boolean supportsPasswordProtected() {
185        return answerForm.hasField(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
186    }
187
188    /**
189     * Set a password and make the room password protected. Users will need to supply the password
190     * to join the room.
191     *
192     * @param password the password to set.
193     * @return a reference to this object.
194     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
195     */
196    public MucConfigFormManager setAndEnablePassword(String password)
197                    throws MucConfigurationNotSupportedException {
198        return setIsPasswordProtected(true).setRoomSecret(password);
199    }
200
201    /**
202     * Make the room password protected.
203     *
204     * @return a reference to this object.
205     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
206     */
207    public MucConfigFormManager makePasswordProtected() throws MucConfigurationNotSupportedException {
208        return setIsPasswordProtected(true);
209    }
210
211    /**
212     * Set if this room is password protected. Rooms are by default not password protected.
213     *
214     * @param isPasswordProtected TODO javadoc me please
215     * @return a reference to this object.
216     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
217     */
218    public MucConfigFormManager setIsPasswordProtected(boolean isPasswordProtected)
219                    throws MucConfigurationNotSupportedException {
220        if (!supportsMembersOnly()) {
221            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
222        }
223        answerForm.setAnswer(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM, isPasswordProtected);
224        return this;
225    }
226
227    /**
228     * Set the room secret, aka the room password. If set and enabled, the password is required to
229     * join the room. Note that this does only set it by does not enable password protection. Use
230     * {@link #setAndEnablePassword(String)} to set a password and make the room protected.
231     *
232     * @param secret the secret/password.
233     * @return a reference to this object.
234     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
235     */
236    public MucConfigFormManager setRoomSecret(String secret)
237                    throws MucConfigurationNotSupportedException {
238        if (!answerForm.hasField(MUC_ROOMCONFIG_ROOMSECRET)) {
239            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMSECRET);
240        }
241        answerForm.setAnswer(MUC_ROOMCONFIG_ROOMSECRET, secret);
242        return this;
243    }
244
245    /**
246     * Submit the configuration as {@link FilledForm} to the room.
247     *
248     * @throws NoResponseException if there was no response from the room.
249     * @throws XMPPErrorException if there was an XMPP error returned.
250     * @throws NotConnectedException if the XMPP connection is not connected.
251     * @throws InterruptedException if the calling thread was interrupted.
252     */
253    public void submitConfigurationForm() throws NoResponseException, XMPPErrorException, NotConnectedException,
254                    InterruptedException {
255        if (owners != null) {
256            answerForm.setAnswer(MUC_ROOMCONFIG_ROOMOWNERS, JidUtil.toStringList(owners));
257        }
258        multiUserChat.sendConfigurationForm(answerForm);
259    }
260}