001/** 002 * 003 * Copyright 2015-2024 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.BooleanFormField; 029import org.jivesoftware.smackx.xdata.FormField; 030import org.jivesoftware.smackx.xdata.form.FillableForm; 031import org.jivesoftware.smackx.xdata.form.FilledForm; 032import org.jivesoftware.smackx.xdata.form.Form; 033 034import org.jxmpp.jid.Jid; 035import org.jxmpp.jid.util.JidUtil; 036 037/** 038 * Multi-User Chat configuration form manager is used to fill out and submit a {@link FilledForm} used to 039 * configure rooms. 040 * <p> 041 * Room configuration needs either be done right after the room is created and still locked. Or at 042 * any later point (see <a href="http://xmpp.org/extensions/xep-0045.html#roomconfig">XEP-45 § 10.2 043 * Subsequent Room Configuration</a>). When done with the configuration, call 044 * {@link #submitConfigurationForm()}. 045 * </p> 046 * <p> 047 * The manager may not provide all possible configuration options. If you want direct access to the 048 * configuration form, use {@link MultiUserChat#getConfigurationForm()} and 049 * {@link MultiUserChat#sendConfigurationForm(FillableForm)}. 050 * </p> 051 */ 052public class MucConfigFormManager { 053 054 private static final String HASH_ROOMCONFIG = "#roomconfig"; 055 056 public static final String FORM_TYPE = MultiUserChatConstants.NAMESPACE + HASH_ROOMCONFIG; 057 058 /** 059 * The constant String {@value}. 060 * 061 * @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a> 062 */ 063 public static final String MUC_ROOMCONFIG_ROOMOWNERS = "muc#roomconfig_roomowners"; 064 065 /** 066 * The constant String {@value}. 067 */ 068 public static final String MUC_ROOMCONFIG_MEMBERSONLY = "muc#roomconfig_membersonly"; 069 070 /** 071 * The constant String {@value}. 072 * 073 * @see <a href="http://xmpp.org/extensions/xep-0045.html#enter-pw">XEP-0045 § 7.2.6 Password-Protected Rooms</a> 074 */ 075 public static final String MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM = "muc#roomconfig_passwordprotectedroom"; 076 077 /** 078 * The constant String {@value}. 079 */ 080 public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret"; 081 082 /** 083 * The constant String {@value}. 084 */ 085 public static final String MUC_ROOMCONFIG_MODERATEDROOM = "muc#roomconfig_moderatedroom"; 086 087 /** 088 * The constant String {@value}. 089 */ 090 public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom"; 091 092 /** 093 * The constant String {@value}. 094 */ 095 public static final String MUC_ROOMCONFIG_ROOMNAME = "muc#roomconfig_roomname"; 096 097 /** 098 * The constant String {@value}. 099 */ 100 public static final String MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING = "muc#roomconfig_enablelogging"; 101 102 /** 103 * The constant String {@value}. 104 */ 105 public static final String MUC_ROOMCONFIG_CHANGE_SUBJECT = "muc#roomconfig_changesubject"; 106 107 private final MultiUserChat multiUserChat; 108 private final FillableForm answerForm; 109 private final List<Jid> owners; 110 111 /** 112 * Create a new MUC config form manager. 113 * <p> 114 * Note that the answerForm needs to be filled out with the defaults. 115 * </p> 116 * 117 * @param multiUserChat the MUC for this configuration form. 118 * @throws InterruptedException if the calling thread was interrupted. 119 * @throws NotConnectedException if the XMPP connection is not connected. 120 * @throws XMPPErrorException if there was an XMPP error returned. 121 * @throws NoResponseException if there was no response from the remote entity. 122 */ 123 MucConfigFormManager(MultiUserChat multiUserChat) throws NoResponseException, 124 XMPPErrorException, NotConnectedException, InterruptedException { 125 this.multiUserChat = multiUserChat; 126 127 // Set the answer form 128 Form configForm = multiUserChat.getConfigurationForm(); 129 this.answerForm = configForm.getFillableForm(); 130 131 // Set the local variables according to the fields found in the answer form 132 FormField roomOwnersFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMOWNERS); 133 if (roomOwnersFormField != null) { 134 // Set 'owners' to the currently configured owners 135 List<? extends CharSequence> ownerStrings = roomOwnersFormField.getValues(); 136 owners = new ArrayList<>(ownerStrings.size()); 137 JidUtil.jidsFrom(ownerStrings, owners, null); 138 } 139 else { 140 // roomowners not supported, this should barely be the case 141 owners = null; 142 } 143 } 144 145 /** 146 * Check if the room supports room owners. 147 * @return <code>true</code> if supported, <code>false</code> if not. 148 * @see #MUC_ROOMCONFIG_ROOMOWNERS 149 */ 150 public boolean supportsRoomOwners() { 151 return owners != null; 152 } 153 154 /** 155 * Set the owners of the room. 156 * 157 * @param newOwners a collection of JIDs to become the new owners of the room. 158 * @return a reference to this object. 159 * @throws MucConfigurationNotSupportedException if the MUC service does not support this option. 160 * @see #MUC_ROOMCONFIG_ROOMOWNERS 161 */ 162 public MucConfigFormManager setRoomOwners(Collection<? extends Jid> newOwners) throws MucConfigurationNotSupportedException { 163 if (!supportsRoomOwners()) { 164 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMOWNERS); 165 } 166 owners.clear(); 167 owners.addAll(newOwners); 168 return this; 169 } 170 171 /** 172 * Check if the room supports a members only configuration. 173 * 174 * @return <code>true</code> if supported, <code>false</code> if not. 175 */ 176 public boolean supportsMembersOnly() { 177 return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY); 178 } 179 180 /** 181 * Check if the room supports being moderated in the configuration. 182 * 183 * @return <code>true</code> if supported, <code>false</code> if not. 184 */ 185 public boolean supportsModeration() { 186 return answerForm.hasField(MUC_ROOMCONFIG_MODERATEDROOM); 187 } 188 189 /** 190 * Make the room for members only. 191 * 192 * @return a reference to this object. 193 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 194 */ 195 public MucConfigFormManager makeMembersOnly() throws MucConfigurationNotSupportedException { 196 return setMembersOnly(true); 197 } 198 199 /** 200 * Set if the room is members only. Rooms are not members only per default. 201 * 202 * @param isMembersOnly if the room should be members only. 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 setMembersOnly(boolean isMembersOnly) throws MucConfigurationNotSupportedException { 207 if (!supportsMembersOnly()) { 208 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MEMBERSONLY); 209 } 210 answerForm.setAnswer(MUC_ROOMCONFIG_MEMBERSONLY, isMembersOnly); 211 return this; 212 } 213 214 215 /** 216 * Make the room moderated. 217 * 218 * @return a reference to this object. 219 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 220 */ 221 public MucConfigFormManager makeModerated() throws MucConfigurationNotSupportedException { 222 return setModerated(true); 223 } 224 225 /** 226 * Set if the room is members only. Rooms are not members only per default. 227 * 228 * @param isModerated if the room should be moderated. 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 setModerated(boolean isModerated) throws MucConfigurationNotSupportedException { 233 if (!supportsModeration()) { 234 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MODERATEDROOM); 235 } 236 answerForm.setAnswer(MUC_ROOMCONFIG_MODERATEDROOM, isModerated); 237 return this; 238 } 239 240 241 /** 242 * Check if the room supports its visibility being controlled vioa configuration. 243 * 244 * @return <code>true</code> if supported, <code>false</code> if not. 245 */ 246 public boolean supportsPublicRoom() { 247 return answerForm.hasField(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM); 248 } 249 250 /** 251 * Make the room publicly searchable. 252 * 253 * @return a reference to this object. 254 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 255 */ 256 public MucConfigFormManager makePublic() throws MucConfigurationNotSupportedException { 257 return setPublic(true); 258 } 259 260 /** 261 * Make the room hidden (not publicly searchable). 262 * 263 * @return a reference to this object. 264 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 265 */ 266 public MucConfigFormManager makeHidden() throws MucConfigurationNotSupportedException { 267 return setPublic(false); 268 } 269 270 /** 271 * Set if the room is publicly searchable (i.e. visible via discovery requests to the MUC service). 272 * 273 * @param isPublic if the room should be publicly searchable. 274 * @return a reference to this object. 275 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 276 */ 277 public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException { 278 if (!supportsPublicRoom()) { 279 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM); 280 } 281 answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic); 282 return this; 283 } 284 285 public boolean supportsRoomname() { 286 return answerForm.hasField(MUC_ROOMCONFIG_ROOMNAME); 287 } 288 289 public MucConfigFormManager setRoomName(String roomName) throws MucConfigurationNotSupportedException { 290 if (!supportsRoomname()) { 291 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMNAME); 292 } 293 answerForm.setAnswer(MUC_ROOMCONFIG_ROOMNAME, roomName); 294 return this; 295 } 296 297 /** 298 * Check if the room supports password protection. 299 * 300 * @return <code>true</code> if supported, <code>false</code> if not. 301 */ 302 public boolean supportsPasswordProtected() { 303 return answerForm.hasField(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM); 304 } 305 306 /** 307 * Set a password and make the room password protected. Users will need to supply the password 308 * to join the room. 309 * 310 * @param password the password to set. 311 * @return a reference to this object. 312 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 313 */ 314 public MucConfigFormManager setAndEnablePassword(String password) 315 throws MucConfigurationNotSupportedException { 316 return setIsPasswordProtected(true).setRoomSecret(password); 317 } 318 319 /** 320 * Make the room password protected. 321 * 322 * @return a reference to this object. 323 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 324 */ 325 public MucConfigFormManager makePasswordProtected() throws MucConfigurationNotSupportedException { 326 return setIsPasswordProtected(true); 327 } 328 329 /** 330 * Set if this room is password protected. Rooms are by default not password protected. 331 * 332 * @param isPasswordProtected TODO javadoc me please 333 * @return a reference to this object. 334 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 335 */ 336 public MucConfigFormManager setIsPasswordProtected(boolean isPasswordProtected) 337 throws MucConfigurationNotSupportedException { 338 if (!supportsPasswordProtected()) { 339 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM); 340 } 341 answerForm.setAnswer(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM, isPasswordProtected); 342 return this; 343 } 344 345 public boolean supportsPublicLogging() { 346 return answerForm.hasField(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING); 347 } 348 349 public MucConfigFormManager setPublicLogging(boolean enabled) throws MucConfigurationNotSupportedException { 350 if (!supportsPublicLogging()) { 351 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING); 352 } 353 answerForm.setAnswer(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING, enabled); 354 return this; 355 } 356 357 public MucConfigFormManager enablePublicLogging() throws MucConfigurationNotSupportedException { 358 return setPublicLogging(true); 359 } 360 361 public MucConfigFormManager disablPublicLogging() throws MucConfigurationNotSupportedException { 362 return setPublicLogging(false); 363 } 364 365 /** 366 * Set the room secret, aka the room password. If set and enabled, the password is required to 367 * join the room. Note that this does only set it by does not enable password protection. Use 368 * {@link #setAndEnablePassword(String)} to set a password and make the room protected. 369 * 370 * @param secret the secret/password. 371 * @return a reference to this object. 372 * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. 373 */ 374 public MucConfigFormManager setRoomSecret(String secret) 375 throws MucConfigurationNotSupportedException { 376 if (!answerForm.hasField(MUC_ROOMCONFIG_ROOMSECRET)) { 377 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMSECRET); 378 } 379 answerForm.setAnswer(MUC_ROOMCONFIG_ROOMSECRET, secret); 380 return this; 381 } 382 383 public boolean supportsChangeSubjectByOccupant() { 384 return answerForm.hasField(MUC_ROOMCONFIG_CHANGE_SUBJECT); 385 } 386 387 public boolean occupantsAreAllowedToChangeSubject() throws MucConfigurationNotSupportedException { 388 if (!supportsChangeSubjectByOccupant()) { 389 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT); 390 } 391 return answerForm.getField(MUC_ROOMCONFIG_CHANGE_SUBJECT).ifPossibleAsOrThrow(BooleanFormField.class).getValueAsBoolean(); 392 } 393 394 public MucConfigFormManager setChangeSubjectByOccupant(boolean enabled) throws MucConfigurationNotSupportedException { 395 if (!supportsChangeSubjectByOccupant()) { 396 throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT); 397 } 398 answerForm.setAnswer(MUC_ROOMCONFIG_CHANGE_SUBJECT, enabled); 399 return this; 400 } 401 402 public MucConfigFormManager allowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException { 403 return setChangeSubjectByOccupant(true); 404 } 405 406 public MucConfigFormManager disallowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException { 407 return setChangeSubjectByOccupant(false); 408 } 409 410 /** 411 * Submit the configuration as {@link FilledForm} to the room. 412 * 413 * @throws NoResponseException if there was no response from the room. 414 * @throws XMPPErrorException if there was an XMPP error returned. 415 * @throws NotConnectedException if the XMPP connection is not connected. 416 * @throws InterruptedException if the calling thread was interrupted. 417 */ 418 public void submitConfigurationForm() throws NoResponseException, XMPPErrorException, NotConnectedException, 419 InterruptedException { 420 if (owners != null) { 421 answerForm.setAnswer(MUC_ROOMCONFIG_ROOMOWNERS, JidUtil.toStringList(owners)); 422 } 423 multiUserChat.sendConfigurationForm(answerForm); 424 } 425}