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