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