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