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    /**
082     * The constant String {@value}.
083     */
084    public static final String MUC_ROOMCONFIG_MODERATEDROOM = "muc#roomconfig_moderatedroom";
085
086    /**
087     * The constant String {@value}.
088     */
089    public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom";
090
091
092    private final MultiUserChat multiUserChat;
093    private final FillableForm answerForm;
094    private final List<Jid> owners;
095
096    /**
097     * Create a new MUC config form manager.
098     * <p>
099     * Note that the answerForm needs to be filled out with the defaults.
100     * </p>
101     *
102     * @param multiUserChat the MUC for this configuration form.
103     * @throws InterruptedException if the calling thread was interrupted.
104     * @throws NotConnectedException if the XMPP connection is not connected.
105     * @throws XMPPErrorException if there was an XMPP error returned.
106     * @throws NoResponseException if there was no response from the remote entity.
107     */
108    MucConfigFormManager(MultiUserChat multiUserChat) throws NoResponseException,
109                    XMPPErrorException, NotConnectedException, InterruptedException {
110        this.multiUserChat = multiUserChat;
111
112        // Set the answer form
113        Form configForm = multiUserChat.getConfigurationForm();
114        this.answerForm = configForm.getFillableForm();
115
116        // Set the local variables according to the fields found in the answer form
117        FormField roomOwnersFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMOWNERS);
118        if (roomOwnersFormField != null) {
119            // Set 'owners' to the currently configured owners
120            List<? extends CharSequence> ownerStrings = roomOwnersFormField.getValues();
121            owners = new ArrayList<>(ownerStrings.size());
122            JidUtil.jidsFrom(ownerStrings, owners, null);
123        }
124        else {
125            // roomowners not supported, this should barely be the case
126            owners = null;
127        }
128    }
129
130    /**
131     * Check if the room supports room owners.
132     * @return <code>true</code> if supported, <code>false</code> if not.
133     * @see #MUC_ROOMCONFIG_ROOMOWNERS
134     */
135    public boolean supportsRoomOwners() {
136        return owners != null;
137    }
138
139    /**
140     * Set the owners of the room.
141     *
142     * @param newOwners a collection of JIDs to become the new owners of the room.
143     * @return a reference to this object.
144     * @throws MucConfigurationNotSupportedException if the MUC service does not support this option.
145     * @see #MUC_ROOMCONFIG_ROOMOWNERS
146     */
147    public MucConfigFormManager setRoomOwners(Collection<? extends Jid> newOwners) throws MucConfigurationNotSupportedException {
148        if (!supportsRoomOwners()) {
149            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMOWNERS);
150        }
151        owners.clear();
152        owners.addAll(newOwners);
153        return this;
154    }
155
156    /**
157     * Check if the room supports a members only configuration.
158     *
159     * @return <code>true</code> if supported, <code>false</code> if not.
160     */
161    public boolean supportsMembersOnly() {
162        return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY);
163    }
164
165    /**
166     * Check if the room supports being moderated in the configuration.
167     *
168     * @return <code>true</code> if supported, <code>false</code> if not.
169     */
170    public boolean supportsModeration() {
171        return answerForm.hasField(MUC_ROOMCONFIG_MODERATEDROOM);
172    }
173
174    /**
175     * Make the room for members only.
176     *
177     * @return a reference to this object.
178     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
179     */
180    public MucConfigFormManager makeMembersOnly() throws MucConfigurationNotSupportedException {
181        return setMembersOnly(true);
182    }
183
184    /**
185     * Set if the room is members only. Rooms are not members only per default.
186     *
187     * @param isMembersOnly if the room should be members only.
188     * @return a reference to this object.
189     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
190     */
191    public MucConfigFormManager setMembersOnly(boolean isMembersOnly) throws MucConfigurationNotSupportedException {
192        if (!supportsMembersOnly()) {
193            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MEMBERSONLY);
194        }
195        answerForm.setAnswer(MUC_ROOMCONFIG_MEMBERSONLY, isMembersOnly);
196        return this;
197    }
198
199
200    /**
201     * Make the room moderated.
202     *
203     * @return a reference to this object.
204     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
205     */
206    public MucConfigFormManager makeModerated() throws MucConfigurationNotSupportedException {
207        return setModerated(true);
208    }
209
210    /**
211     * Set if the room is members only. Rooms are not members only per default.
212     *
213     * @param isModerated if the room should be moderated.
214     * @return a reference to this object.
215     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
216     */
217    public MucConfigFormManager setModerated(boolean isModerated) throws MucConfigurationNotSupportedException {
218        if (!supportsModeration()) {
219            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MODERATEDROOM);
220        }
221        answerForm.setAnswer(MUC_ROOMCONFIG_MODERATEDROOM, isModerated);
222        return this;
223    }
224
225
226    /**
227     * Make the room publicly searchable.
228     *
229     * @return a reference to this object.
230     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
231     */
232    public MucConfigFormManager makePublic() throws MucConfigurationNotSupportedException {
233        return setPublic(true);
234    }
235
236    /**
237     * Make the room hidden (not publicly searchable).
238     *
239     * @return a reference to this object.
240     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
241     */
242    public MucConfigFormManager makeHidden() throws MucConfigurationNotSupportedException {
243        return setPublic(false);
244    }
245
246    /**
247     * Set if the room is publicly searchable (i.e. visible via discovery requests to the MUC service).
248     *
249     * @param isPublic if the room should be publicly searchable.
250     * @return a reference to this object.
251     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
252     */
253    public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException {
254        if (!supportsModeration()) {
255            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM);
256        }
257        answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic);
258        return this;
259    }
260
261    /**
262     * Check if the room supports password protection.
263     *
264     * @return <code>true</code> if supported, <code>false</code> if not.
265     */
266    public boolean supportsPasswordProtected() {
267        return answerForm.hasField(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
268    }
269
270    /**
271     * Set a password and make the room password protected. Users will need to supply the password
272     * to join the room.
273     *
274     * @param password the password to set.
275     * @return a reference to this object.
276     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
277     */
278    public MucConfigFormManager setAndEnablePassword(String password)
279                    throws MucConfigurationNotSupportedException {
280        return setIsPasswordProtected(true).setRoomSecret(password);
281    }
282
283    /**
284     * Make the room password protected.
285     *
286     * @return a reference to this object.
287     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
288     */
289    public MucConfigFormManager makePasswordProtected() throws MucConfigurationNotSupportedException {
290        return setIsPasswordProtected(true);
291    }
292
293    /**
294     * Set if this room is password protected. Rooms are by default not password protected.
295     *
296     * @param isPasswordProtected TODO javadoc me please
297     * @return a reference to this object.
298     * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
299     */
300    public MucConfigFormManager setIsPasswordProtected(boolean isPasswordProtected)
301                    throws MucConfigurationNotSupportedException {
302        if (!supportsMembersOnly()) {
303            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM);
304        }
305        answerForm.setAnswer(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM, isPasswordProtected);
306        return this;
307    }
308
309    /**
310     * Set the room secret, aka the room password. If set and enabled, the password is required to
311     * join the room. Note that this does only set it by does not enable password protection. Use
312     * {@link #setAndEnablePassword(String)} to set a password and make the room protected.
313     *
314     * @param secret the secret/password.
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 setRoomSecret(String secret)
319                    throws MucConfigurationNotSupportedException {
320        if (!answerForm.hasField(MUC_ROOMCONFIG_ROOMSECRET)) {
321            throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMSECRET);
322        }
323        answerForm.setAnswer(MUC_ROOMCONFIG_ROOMSECRET, secret);
324        return this;
325    }
326
327    /**
328     * Submit the configuration as {@link FilledForm} to the room.
329     *
330     * @throws NoResponseException if there was no response from the room.
331     * @throws XMPPErrorException if there was an XMPP error returned.
332     * @throws NotConnectedException if the XMPP connection is not connected.
333     * @throws InterruptedException if the calling thread was interrupted.
334     */
335    public void submitConfigurationForm() throws NoResponseException, XMPPErrorException, NotConnectedException,
336                    InterruptedException {
337        if (owners != null) {
338            answerForm.setAnswer(MUC_ROOMCONFIG_ROOMOWNERS, JidUtil.toStringList(owners));
339        }
340        multiUserChat.sendConfigurationForm(answerForm);
341    }
342}