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