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