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}