001/** 002 * 003 * Copyright 2003-2007 Jive Software. 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 */ 017 018package org.jivesoftware.smackx.muc; 019 020import java.net.MalformedURLException; 021import java.net.URL; 022import java.util.List; 023import java.util.logging.Level; 024import java.util.logging.Logger; 025 026import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 027import org.jivesoftware.smackx.xdata.Form; 028import org.jivesoftware.smackx.xdata.FormField; 029 030/** 031 * Represents the room information that was discovered using Service Discovery. It's possible to 032 * obtain information about a room before joining the room but only for rooms that are public (i.e. 033 * rooms that may be discovered). 034 * 035 * @author Gaston Dombiak 036 */ 037public class RoomInfo { 038 039 private static final Logger LOGGER = Logger.getLogger(RoomInfo.class.getName()); 040 041 /** 042 * JID of the room. The node of the JID is commonly used as the ID of the room or name. 043 */ 044 private final String room; 045 /** 046 * Description of the room. 047 */ 048 private final String description; 049 050 /** 051 * Name of the room. 052 */ 053 private final String name; 054 055 /** 056 * Last known subject of the room. 057 */ 058 private final String subject; 059 /** 060 * Current number of occupants in the room. 061 */ 062 private final int occupantsCount; 063 /** 064 * A room is considered members-only if an invitation is required in order to enter the room. 065 * Any user that is not a member of the room won't be able to join the room unless the user 066 * decides to register with the room (thus becoming a member). 067 */ 068 private final boolean membersOnly; 069 /** 070 * Moderated rooms enable only participants to speak. Users that join the room and aren't 071 * participants can't speak (they are just visitors). 072 */ 073 private final boolean moderated; 074 /** 075 * Every presence stanza(/packet) can include the JID of every occupant unless the owner deactives this 076 * configuration. 077 */ 078 private final boolean nonanonymous; 079 /** 080 * Indicates if users must supply a password to join the room. 081 */ 082 private final boolean passwordProtected; 083 /** 084 * Persistent rooms are saved to the database to make sure that rooms configurations can be 085 * restored in case the server goes down. 086 */ 087 private final boolean persistent; 088 089 /** 090 * Maximum number of history messages returned by the room. 091 */ 092 private final int maxhistoryfetch; 093 094 /** 095 * Contact Address 096 */ 097 private final List<String> contactJid; 098 099 /** 100 * Natural Language for Room Discussions 101 */ 102 private final String lang; 103 104 /** 105 * An associated LDAP group that defined room membership. Should be an LDAP 106 * Distinguished Name 107 */ 108 private final String ldapgroup; 109 110 /** 111 * True if the room subject can be modified by participants 112 */ 113 private final Boolean subjectmod; 114 115 /** 116 * URL for archived discussion logs 117 */ 118 private final URL logs; 119 120 /** 121 * An associated pubsub node 122 */ 123 private final String pubsub; 124 125 /** 126 * The rooms extended configuration form; 127 */ 128 private final Form form; 129 130 RoomInfo(DiscoverInfo info) { 131 this.room = info.getFrom(); 132 // Get the information based on the discovered features 133 this.membersOnly = info.containsFeature("muc_membersonly"); 134 this.moderated = info.containsFeature("muc_moderated"); 135 this.nonanonymous = info.containsFeature("muc_nonanonymous"); 136 this.passwordProtected = info.containsFeature("muc_passwordprotected"); 137 this.persistent = info.containsFeature("muc_persistent"); 138 139 List<DiscoverInfo.Identity> identities = info.getIdentities(); 140 // XEP-45 6.4 is not really clear on the topic if an identity needs to 141 // be send together with the disco result and how to call this description. 142 if (!identities.isEmpty()) { 143 this.name = identities.get(0).getName(); 144 } else { 145 LOGGER.warning("DiscoverInfo does not contain any Identity: " + info.toXML()); 146 this.name = ""; 147 } 148 String subject = ""; 149 int occupantsCount = -1; 150 String description = ""; 151 int maxhistoryfetch = -1; 152 List<String> contactJid = null; 153 String lang = null; 154 String ldapgroup = null; 155 Boolean subjectmod = null; 156 URL logs = null; 157 String pubsub = null; 158 // Get the information based on the discovered extended information 159 form = Form.getFormFrom(info); 160 if (form != null) { 161 FormField descField = form.getField("muc#roominfo_description"); 162 if (descField != null && !descField.getValues().isEmpty()) { 163 // Prefer the extended result description 164 description = descField.getValues().get(0); 165 } 166 167 FormField subjField = form.getField("muc#roominfo_subject"); 168 if (subjField != null && !subjField.getValues().isEmpty()) { 169 subject = subjField.getValues().get(0); 170 } 171 172 FormField occCountField = form.getField("muc#roominfo_occupants"); 173 if (occCountField != null && !occCountField.getValues().isEmpty()) { 174 occupantsCount = Integer.parseInt(occCountField.getValues().get( 175 0)); 176 } 177 178 FormField maxhistoryfetchField = form.getField("muc#maxhistoryfetch"); 179 if (maxhistoryfetchField != null && !maxhistoryfetchField.getValues().isEmpty()) { 180 maxhistoryfetch = Integer.parseInt(maxhistoryfetchField.getValues().get( 181 0)); 182 } 183 184 FormField contactJidField = form.getField("muc#roominfo_contactjid"); 185 if (contactJidField != null && !contactJidField.getValues().isEmpty()) { 186 contactJid = contactJidField.getValues(); 187 } 188 189 FormField langField = form.getField("muc#roominfo_lang"); 190 if (langField != null && !langField.getValues().isEmpty()) { 191 lang = langField.getValues().get(0); 192 } 193 194 FormField ldapgroupField = form.getField("muc#roominfo_ldapgroup"); 195 if (ldapgroupField != null && !ldapgroupField.getValues().isEmpty()) { 196 ldapgroup = ldapgroupField.getValues().get(0); 197 } 198 199 FormField subjectmodField = form.getField("muc#roominfo_subjectmod"); 200 if (subjectmodField != null && !subjectmodField.getValues().isEmpty()) { 201 subjectmod = Boolean.valueOf(subjectmodField.getValues().get(0)); 202 } 203 204 FormField urlField = form.getField("muc#roominfo_logs"); 205 if (urlField != null && !urlField.getValues().isEmpty()) { 206 String urlString = urlField.getValues().get(0); 207 try { 208 logs = new URL(urlString); 209 } catch (MalformedURLException e) { 210 LOGGER.log(Level.SEVERE, "Could not parse URL", e); 211 } 212 } 213 214 FormField pubsubField = form.getField("muc#roominfo_pubsub"); 215 if (pubsubField != null && !pubsubField.getValues().isEmpty()) { 216 pubsub = pubsubField.getValues().get(0); 217 } 218 } 219 this.description = description; 220 this.subject = subject; 221 this.occupantsCount = occupantsCount; 222 this.maxhistoryfetch = maxhistoryfetch; 223 this.contactJid = contactJid; 224 this.lang = lang; 225 this.ldapgroup = ldapgroup; 226 this.subjectmod = subjectmod; 227 this.logs = logs; 228 this.pubsub = pubsub; 229 } 230 231 /** 232 * Returns the JID of the room whose information was discovered. 233 * 234 * @return the JID of the room whose information was discovered. 235 */ 236 public String getRoom() { 237 return room; 238 } 239 240 /** 241 * Returns the room name. 242 * <p> 243 * The name returnd here was provided as value of the name attribute 244 * of the returned identity within the disco#info result. 245 * </p> 246 * 247 * @return the name of the room. 248 */ 249 public String getName() { 250 return name; 251 } 252 253 /** 254 * Returns the discovered description of the room. 255 * <p> 256 * The description returned by this method was provided as value of the form 257 * field of the extended disco info result. It may be <code>null</code>. 258 * </p> 259 * 260 * @return the discovered description of the room or null 261 */ 262 public String getDescription() { 263 return description; 264 } 265 266 /** 267 * Returns the discovered subject of the room. The subject may be null if the room does not 268 * have a subject. 269 * 270 * @return the discovered subject of the room or null 271 */ 272 public String getSubject() { 273 return subject; 274 } 275 276 /** 277 * Returns the discovered number of occupants that are currently in the room. If this 278 * information was not discovered (i.e. the server didn't send it) then a value of -1 will be 279 * returned. 280 * 281 * @return the number of occupants that are currently in the room or -1 if that information was 282 * not provided by the server. 283 */ 284 public int getOccupantsCount() { 285 return occupantsCount; 286 } 287 288 /** 289 * Returns true if the room has restricted the access so that only members may enter the room. 290 * 291 * @return true if the room has restricted the access so that only members may enter the room. 292 */ 293 public boolean isMembersOnly() { 294 return membersOnly; 295 } 296 297 /** 298 * Returns true if the room enabled only participants to speak. Occupants with a role of 299 * visitor won't be able to speak in the room. 300 * 301 * @return true if the room enabled only participants to speak. 302 */ 303 public boolean isModerated() { 304 return moderated; 305 } 306 307 /** 308 * Returns true if presence packets will include the JID of every occupant. 309 * 310 * @return true if presence packets will include the JID of every occupant. 311 */ 312 public boolean isNonanonymous() { 313 return nonanonymous; 314 } 315 316 /** 317 * Returns true if users musy provide a valid password in order to join the room. 318 * 319 * @return true if users musy provide a valid password in order to join the room. 320 */ 321 public boolean isPasswordProtected() { 322 return passwordProtected; 323 } 324 325 /** 326 * Returns true if the room will persist after the last occupant have left the room. 327 * 328 * @return true if the room will persist after the last occupant have left the room. 329 */ 330 public boolean isPersistent() { 331 return persistent; 332 } 333 334 /** 335 * Returns the maximum number of history messages which are returned by the 336 * room or '-1' if this property is not reported by the room. 337 * 338 * @return the maximum number of history messages or '-1' 339 */ 340 public int getMaxHistoryFetch() { 341 return maxhistoryfetch; 342 } 343 344 /** 345 * Returns Contact Addresses as JIDs, if such are reported. 346 * 347 * @return a list of contact addresses for this room. 348 */ 349 public List<String> getContactJids() { 350 return contactJid; 351 } 352 353 /** 354 * Returns the natural language of the room discussion, or <code>null</code>. 355 * 356 * @return the language of the room discussion or <code>null</code>. 357 */ 358 public String getLang() { 359 return lang; 360 } 361 362 /** 363 * Returns an associated LDAP group that defines room membership. The 364 * value should be an LDAP Distinguished Name according to an 365 * implementation-specific or deployment-specific definition of a group. 366 * 367 * @return an associated LDAP group or <code>null</code> 368 */ 369 public String getLdapGroup() { 370 return ldapgroup; 371 } 372 373 /** 374 * Returns an Boolean instance with the value 'true' if the subject can be 375 * modified by the room participants, 'false' if not, or <code>null</code> 376 * if this information is reported by the room. 377 * 378 * @return an boolean that is true if the subject can be modified by 379 * participants or <code>null</code> 380 */ 381 public Boolean isSubjectModifiable() { 382 return subjectmod; 383 } 384 385 /** 386 * An associated pubsub node for this room or <code>null</code>. 387 * 388 * @return the associated pubsub node or <code>null</code> 389 */ 390 public String getPubSub() { 391 return pubsub; 392 } 393 394 /** 395 * Returns the URL where archived discussion logs can be found or 396 * <code>null</code> if there is no such URL. 397 * 398 * @return the URL where archived logs can be found or <code>null</code> 399 */ 400 public URL getLogsUrl() { 401 return logs; 402 } 403 404 /** 405 * Returns the form included in the extended disco info result or 406 * <code>null</code> if no such form was sent. 407 * 408 * @return The room info form or <code>null</code> 409 * @see <a 410 * href="http://xmpp.org/extensions/xep-0045.html#disco-roominfo">XEP-45: 411 * Multi User Chat - 6.5 Querying for Room Information</a> 412 */ 413 public Form getForm() { 414 return form; 415 } 416 417}