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.packet; 019 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024 025import javax.xml.namespace.QName; 026 027import org.jivesoftware.smack.packet.ExtensionElement; 028import org.jivesoftware.smack.packet.NamedElement; 029import org.jivesoftware.smack.packet.Stanza; 030import org.jivesoftware.smack.util.XmlStringBuilder; 031 032import org.jxmpp.jid.EntityBareJid; 033import org.jxmpp.jid.EntityFullJid; 034import org.jxmpp.jid.EntityJid; 035 036/** 037 * Represents extended presence information about roles, affiliations, full JIDs, 038 * or status codes scoped by the 'http://jabber.org/protocol/muc#user' namespace. 039 * 040 * @author Gaston Dombiak 041 */ 042public class MUCUser implements ExtensionElement { 043 044 public static final String ELEMENT = "x"; 045 public static final String NAMESPACE = MUCInitialPresence.NAMESPACE + "#user"; 046 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 047 048 private final Set<Status> statusCodes = new HashSet<>(4); 049 050 private Invite invite; 051 private Decline decline; 052 private MUCItem item; 053 private String password; 054 private Destroy destroy; 055 056 @Override 057 public String getElementName() { 058 return ELEMENT; 059 } 060 061 @Override 062 public String getNamespace() { 063 return NAMESPACE; 064 } 065 066 @Override 067 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 068 XmlStringBuilder xml = new XmlStringBuilder(this); 069 xml.rightAngleBracket(); 070 xml.optElement(getInvite()); 071 xml.optElement(getDecline()); 072 xml.optElement(getItem()); 073 xml.optElement("password", getPassword()); 074 xml.append(statusCodes); 075 xml.optElement(getDestroy()); 076 xml.closeElement(this); 077 return xml; 078 } 079 080 /** 081 * Returns the invitation for another user to a room. The sender of the invitation 082 * must be an occupant of the room. The invitation will be sent to the room which in turn 083 * will forward the invitation to the invitee. 084 * 085 * @return an invitation for another user to a room. 086 */ 087 public Invite getInvite() { 088 return invite; 089 } 090 091 /** 092 * Returns the rejection to an invitation from another user to a room. The rejection will be 093 * sent to the room which in turn will forward the refusal to the inviting user. 094 * 095 * @return a rejection to an invitation from another user to a room. 096 */ 097 public Decline getDecline() { 098 return decline; 099 } 100 101 /** 102 * Returns the item child that holds information about roles, affiliation, jids and nicks. 103 * 104 * @return an item child that holds information about roles, affiliation, jids and nicks. 105 */ 106 public MUCItem getItem() { 107 return item; 108 } 109 110 /** 111 * Returns the password to use to enter Password-Protected Room. A Password-Protected Room is 112 * a room that a user cannot enter without first providing the correct password. 113 * 114 * @return the password to use to enter Password-Protected Room. 115 */ 116 public String getPassword() { 117 return password; 118 } 119 120 /** 121 * Returns a set of status which holds the status code that assist in presenting notification messages. 122 * 123 * @return the set of status which holds the status code that assist in presenting notification messages. 124 */ 125 public Set<Status> getStatus() { 126 return statusCodes; 127 } 128 129 /** 130 * Returns true if this MUCUser instance has also {@link Status} information. 131 * <p> 132 * If <code>true</code> is returned, then {@link #getStatus()} will return a non-empty set. 133 * </p> 134 * 135 * @return true if this MUCUser has status information. 136 * @since 4.1 137 */ 138 public boolean hasStatus() { 139 return !statusCodes.isEmpty(); 140 } 141 142 /** 143 * Returns the notification that the room has been destroyed. After a room has been destroyed, 144 * the room occupants will receive a Presence stanza of type 'unavailable' with the reason for 145 * the room destruction if provided by the room owner. 146 * 147 * @return a notification that the room has been destroyed. 148 */ 149 public Destroy getDestroy() { 150 return destroy; 151 } 152 153 /** 154 * Sets the invitation for another user to a room. The sender of the invitation 155 * must be an occupant of the room. The invitation will be sent to the room which in turn 156 * will forward the invitation to the invitee. 157 * 158 * @param invite the invitation for another user to a room. 159 */ 160 public void setInvite(Invite invite) { 161 this.invite = invite; 162 } 163 164 /** 165 * Sets the rejection to an invitation from another user to a room. The rejection will be 166 * sent to the room which in turn will forward the refusal to the inviting user. 167 * 168 * @param decline the rejection to an invitation from another user to a room. 169 */ 170 public void setDecline(Decline decline) { 171 this.decline = decline; 172 } 173 174 /** 175 * Sets the item child that holds information about roles, affiliation, jids and nicks. 176 * 177 * @param item the item child that holds information about roles, affiliation, jids and nicks. 178 */ 179 public void setItem(MUCItem item) { 180 this.item = item; 181 } 182 183 /** 184 * Sets the password to use to enter Password-Protected Room. A Password-Protected Room is 185 * a room that a user cannot enter without first providing the correct password. 186 * 187 * @param string the password to use to enter Password-Protected Room. 188 */ 189 public void setPassword(String string) { 190 password = string; 191 } 192 193 /** 194 * Add the status codes which holds the codes that assists in presenting notification messages. 195 * 196 * @param statusCodes the status codes which hold the codes that assists in presenting notification 197 * messages. 198 */ 199 public void addStatusCodes(Set<Status> statusCodes) { 200 this.statusCodes.addAll(statusCodes); 201 } 202 203 /** 204 * Add a status code which hold a code that assists in presenting notification messages. 205 * 206 * @param status the status code which olds a code that assists in presenting notification messages. 207 */ 208 public void addStatusCode(Status status) { 209 this.statusCodes.add(status); 210 } 211 212 /** 213 * Sets the notification that the room has been destroyed. After a room has been destroyed, 214 * the room occupants will receive a Presence stanza of type 'unavailable' with the reason for 215 * the room destruction if provided by the room owner. 216 * 217 * @param destroy the notification that the room has been destroyed. 218 */ 219 public void setDestroy(Destroy destroy) { 220 this.destroy = destroy; 221 } 222 223 /** 224 * Retrieve the MUCUser PacketExtension from packet, if any. 225 * 226 * @param packet TODO javadoc me please 227 * @return the MUCUser PacketExtension or {@code null} 228 * @deprecated use {@link #from(Stanza)} instead 229 */ 230 @Deprecated 231 public static MUCUser getFrom(Stanza packet) { 232 return from(packet); 233 } 234 235 /** 236 * Retrieve the MUCUser PacketExtension from packet, if any. 237 * 238 * @param packet TODO javadoc me please 239 * @return the MUCUser PacketExtension or {@code null} 240 */ 241 public static MUCUser from(Stanza packet) { 242 return packet.getExtension(MUCUser.class); 243 } 244 245 /** 246 * Represents an invitation for another user to a room. The sender of the invitation 247 * must be an occupant of the room. The invitation will be sent to the room which in turn 248 * will forward the invitation to the invitee. 249 * 250 * @author Gaston Dombiak 251 */ 252 public static class Invite implements NamedElement { 253 public static final String ELEMENT = "invite"; 254 255 private final String reason; 256 257 /** 258 * From XEP-0045 § 7.8.2: "… whose value is the bare JID, full JID, or occupant JID of the inviting user …" 259 */ 260 private final EntityJid from; 261 262 private final EntityBareJid to; 263 264 public Invite(String reason, EntityFullJid from) { 265 this(reason, from, null); 266 } 267 268 public Invite(String reason, EntityBareJid to) { 269 this(reason, null, to); 270 } 271 272 public Invite(String reason, EntityJid from, EntityBareJid to) { 273 this.reason = reason; 274 this.from = from; 275 this.to = to; 276 } 277 278 /** 279 * Returns the bare JID of the inviting user or, optionally, the room JID. (e.g. 280 * 'crone1@shakespeare.lit/desktop'). 281 * 282 * @return the room's occupant that sent the invitation. 283 */ 284 public EntityJid getFrom() { 285 return from; 286 } 287 288 /** 289 * Returns the message explaining the invitation. 290 * 291 * @return the message explaining the invitation. 292 */ 293 public String getReason() { 294 return reason; 295 } 296 297 /** 298 * Returns the bare JID of the invitee. (e.g. 'hecate@shakespeare.lit') 299 * 300 * @return the bare JID of the invitee. 301 */ 302 public EntityBareJid getTo() { 303 return to; 304 } 305 306 @Override 307 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 308 XmlStringBuilder xml = new XmlStringBuilder(this); 309 xml.optAttribute("to", getTo()); 310 xml.optAttribute("from", getFrom()); 311 xml.rightAngleBracket(); 312 xml.optElement("reason", getReason()); 313 xml.closeElement(this); 314 return xml; 315 } 316 317 @Override 318 public String getElementName() { 319 return ELEMENT; 320 } 321 } 322 323 /** 324 * Represents a rejection to an invitation from another user to a room. The rejection will be 325 * sent to the room which in turn will forward the refusal to the inviting user. 326 * 327 * @author Gaston Dombiak 328 */ 329 public static class Decline implements ExtensionElement { 330 public static final String ELEMENT = "decline"; 331 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 332 333 private final String reason; 334 private final EntityBareJid from; 335 private final EntityBareJid to; 336 337 public Decline(String reason, EntityBareJid to) { 338 this(reason, null, to); 339 } 340 341 public Decline(String reason, EntityBareJid from, EntityBareJid to) { 342 this.reason = reason; 343 this.from = from; 344 this.to = to; 345 } 346 347 /** 348 * Returns the bare JID of the invitee that rejected the invitation. (e.g. 349 * 'crone1@shakespeare.lit'). 350 * 351 * @return the bare JID of the invitee that rejected the invitation. 352 */ 353 public EntityBareJid getFrom() { 354 return from; 355 } 356 357 /** 358 * Returns the message explaining why the invitation was rejected. 359 * 360 * @return the message explaining the reason for the rejection. 361 */ 362 public String getReason() { 363 return reason; 364 } 365 366 /** 367 * Returns the bare JID of the inviting user. (e.g. 'hecate@shakespeare.lit') 368 * 369 * @return the bare JID of the inviting user. 370 */ 371 public EntityBareJid getTo() { 372 return to; 373 } 374 375 @Override 376 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 377 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 378 xml.optAttribute("to", getTo()); 379 xml.optAttribute("from", getFrom()); 380 xml.rightAngleBracket(); 381 xml.optElement("reason", getReason()); 382 xml.closeElement(this); 383 return xml; 384 } 385 386 @Override 387 public String getElementName() { 388 return QNAME.getLocalPart(); 389 } 390 391 @Override 392 public String getNamespace() { 393 return QNAME.getNamespaceURI(); 394 } 395 } 396 397 /** 398 * Status code assists in presenting notification messages. The following link provides the 399 * list of existing error codes <a href="http://xmpp.org/registrar/mucstatus.html">Multi-User Chat Status Codes</a>. 400 * 401 * @author Gaston Dombiak 402 */ 403 public static final class Status implements NamedElement { 404 public static final String ELEMENT = "status"; 405 406 private static final Map<Integer, Status> statusMap = new HashMap<>(8); 407 408 public static final Status PRESENCE_TO_SELF_110 = Status.create(110); 409 public static final Status ROOM_CREATED_201 = Status.create(201); 410 public static final Status BANNED_301 = Status.create(301); 411 public static final Status NEW_NICKNAME_303 = Status.create(303); 412 public static final Status KICKED_307 = Status.create(307); 413 public static final Status REMOVED_AFFIL_CHANGE_321 = Status.create(321); 414 public static final Status REMOVED_FOR_TECHNICAL_REASONS_333 = Status.create(333); 415 416 private final Integer code; 417 418 public static Status create(String string) { 419 Integer integer = Integer.valueOf(string); 420 return create(integer); 421 } 422 423 public static Status create(Integer i) { 424 Status status; 425 // TODO: Use computeIfAbsent once Smack's minimum required Android SDK level is 24 or higher. 426 synchronized (statusMap) { 427 status = statusMap.get(i); 428 if (status == null) { 429 status = new Status(i); 430 statusMap.put(i, status); 431 } 432 } 433 return status; 434 } 435 436 /** 437 * Creates a new instance of Status with the specified code. 438 * 439 * @param code the code that uniquely identifies the reason of the error. 440 */ 441 private Status(int code) { 442 this.code = code; 443 } 444 445 /** 446 * Returns the code that uniquely identifies the reason of the error. The code 447 * assists in presenting notification messages. 448 * 449 * @return the code that uniquely identifies the reason of the error. 450 */ 451 public int getCode() { 452 return code; 453 } 454 455 @Override 456 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 457 XmlStringBuilder xml = new XmlStringBuilder(this); 458 xml.attribute("code", getCode()); 459 xml.closeEmptyElement(); 460 return xml; 461 } 462 463 @Override 464 public String toString() { 465 return code.toString(); 466 } 467 468 @Override 469 public boolean equals(Object other) { 470 if (other == null) { 471 return false; 472 } 473 if (other instanceof Status) { 474 Status otherStatus = (Status) other; 475 return code.equals(otherStatus.getCode()); 476 } 477 return false; 478 } 479 480 @Override 481 public int hashCode() { 482 return code; 483 } 484 485 @Override 486 public String getElementName() { 487 return ELEMENT; 488 } 489 } 490}