001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2020 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 */ 017 018package org.jivesoftware.smack.packet; 019 020import java.util.List; 021import java.util.Locale; 022 023import org.jivesoftware.smack.XMPPConnection; 024import org.jivesoftware.smack.util.Objects; 025import org.jivesoftware.smack.util.StringUtils; 026import org.jivesoftware.smack.util.XmlStringBuilder; 027 028import org.jxmpp.jid.Jid; 029 030/** 031 * Represents XMPP presence stanzas. Every presence stanza has a type, which is one of 032 * the following values: 033 * <ul> 034 * <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to 035 * receive messages. 036 * <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages. 037 * <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence. 038 * <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence. 039 * <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to 040 * sender's presence. 041 * <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to 042 * sender's presence. 043 * <li>{@link Presence.Type#error error} -- the presence stanza contains an error message. 044 * </ul><p> 045 * 046 * A number of attributes are optional: 047 * <ul> 048 * <li>Status -- free-form text describing a user's presence (i.e., gone to lunch). 049 * <li>Priority -- non-negative numerical priority of a sender's resource. The 050 * highest resource priority is the default recipient of packets not addressed 051 * to a particular resource. 052 * <li>Mode -- one of five presence modes: {@link Mode#available available} (the default), 053 * {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and 054 * {@link Mode#dnd dnd} (do not disturb). 055 * </ul><p> 056 * 057 * Presence stanzas are used for two purposes. First, to notify the server of 058 * the user's current presence status. Second, they are used to subscribe and 059 * unsubscribe users from the roster. 060 * 061 * @author Matt Tucker 062 */ 063public final class Presence extends MessageOrPresence<PresenceBuilder> 064 implements PresenceView { 065 066 public static final String ELEMENT = "presence"; 067 068 private Type type = Type.available; 069 private String status = null; 070 071 /** 072 * The priority of the presence. It is <code>null</code> to indicate that the original 073 * presence stanza did not had an explicit priority set. In which case the priority defaults to 0. 074 * 075 * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3.</a> 076 */ 077 private Byte priority; 078 079 private Mode mode = null; 080 081 /** 082 * Creates a new presence update. Status, priority, and mode are left un-set. 083 * 084 * @param type the type. 085 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 086 */ 087 @Deprecated 088 // TODO: Remove in Smack 4.5. 089 public Presence(Type type) { 090 // Ensure that the stanza ID is set by calling super(). 091 super(); 092 setType(type); 093 } 094 095 /** 096 * Creates a new presence with the given type and using the given XMPP address as recipient. 097 * 098 * @param to the recipient. 099 * @param type the type. 100 * @since 4.2 101 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 102 */ 103 @Deprecated 104 // TODO: Remove in Smack 4.5. 105 public Presence(Jid to, Type type) { 106 this(type); 107 setTo(to); 108 } 109 110 /** 111 * Creates a new presence update with a specified status, priority, and mode. 112 * 113 * @param type the type. 114 * @param status a text message describing the presence update. 115 * @param priority the priority of this presence update. 116 * @param mode the mode type for this presence update. 117 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 118 */ 119 @Deprecated 120 // TODO: Remove in Smack 4.5. 121 public Presence(Type type, String status, int priority, Mode mode) { 122 // Ensure that the stanza ID is set by calling super(). 123 super(); 124 setType(type); 125 setStatus(status); 126 setPriority(priority); 127 setMode(mode); 128 } 129 130 Presence(PresenceBuilder presenceBuilder) { 131 super(presenceBuilder); 132 type = presenceBuilder.type; 133 status = presenceBuilder.status; 134 priority = presenceBuilder.priority; 135 mode = presenceBuilder.mode; 136 } 137 138 /** 139 * Copy constructor. 140 * <p> 141 * This does not perform a deep clone, as extension elements are shared between the new and old 142 * instance. 143 * </p> 144 * 145 * @param other TODO javadoc me please 146 */ 147 public Presence(Presence other) { 148 super(other); 149 this.type = other.type; 150 this.status = other.status; 151 this.priority = other.priority; 152 this.mode = other.mode; 153 } 154 155 /** 156 * Returns true if the {@link Type presence type} is available (online) and 157 * false if the user is unavailable (offline), or if this is a presence packet 158 * involved in a subscription operation. This is a convenience method 159 * equivalent to <code>getType() == Presence.Type.available</code>. Note that even 160 * when the user is available, their presence mode may be {@link Mode#away away}, 161 * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use 162 * {@link #isAway()} to determine if the user is away. 163 * 164 * @return true if the presence type is available. 165 */ 166 public boolean isAvailable() { 167 return type == Type.available; 168 } 169 170 /** 171 * Returns true if the presence type is {@link Type#available available} and the presence 172 * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or 173 * {@link Mode#dnd do not disturb}. False will be returned when the type or mode 174 * is any other value, including when the presence type is unavailable (offline). 175 * This is a convenience method equivalent to 176 * <code>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</code>. 177 * 178 * @return true if the presence type is available and the presence mode is away, xa, or dnd. 179 */ 180 public boolean isAway() { 181 return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd); 182 } 183 184 @Override 185 public Type getType() { 186 return type; 187 } 188 189 /** 190 * Sets the type of the presence packet. 191 * 192 * @param type the type of the presence packet. 193 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 194 */ 195 @Deprecated 196 // TODO: Remove in Smack 4.5. 197 public void setType(Type type) { 198 this.type = Objects.requireNonNull(type, "Type cannot be null"); 199 } 200 201 @Override 202 public String getStatus() { 203 return status; 204 } 205 206 /** 207 * Sets the status message of the presence update. The status is free-form text 208 * describing a user's presence (i.e., "gone to lunch"). 209 * 210 * @param status the status message. 211 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 212 */ 213 @Deprecated 214 // TODO: Remove in Smack 4.5. 215 public void setStatus(String status) { 216 this.status = status; 217 } 218 219 @Override 220 public int getPriority() { 221 return getPriorityByte(); 222 } 223 224 @Override 225 public byte getPriorityByte() { 226 if (priority == null) { 227 return 0; 228 } 229 return priority; 230 } 231 232 /** 233 * Sets the priority of the presence. The valid range is -128 through 127. 234 * 235 * @param priority the priority of the presence. 236 * @throws IllegalArgumentException if the priority is outside the valid range. 237 * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a> 238 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 239 */ 240 @Deprecated 241 // TODO: Remove in Smack 4.5. 242 public void setPriority(int priority) { 243 if (priority < -128 || priority > 127) { 244 throw new IllegalArgumentException("Priority value " + priority + 245 " is not valid. Valid range is -128 through 127."); 246 } 247 setPriority((byte) priority); 248 } 249 250 public void setPriority(byte priority) { 251 this.priority = priority; 252 } 253 254 @Override 255 public Mode getMode() { 256 if (mode == null) { 257 return Mode.available; 258 } 259 return mode; 260 } 261 262 /** 263 * Sets the mode of the presence update. A null presence mode value is interpreted 264 * to be the same thing as {@link Presence.Mode#available}. 265 * 266 * @param mode the mode. 267 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 268 */ 269 @Deprecated 270 // TODO: Remove in Smack 4.5. 271 public void setMode(Mode mode) { 272 this.mode = mode; 273 } 274 275 @Override 276 public String getElementName() { 277 return ELEMENT; 278 } 279 280 @Override 281 public PresenceBuilder asBuilder() { 282 return StanzaBuilder.buildPresenceFrom(this, getStanzaId()); 283 } 284 285 @Override 286 public PresenceBuilder asBuilder(String id) { 287 return StanzaBuilder.buildPresenceFrom(this, id); 288 } 289 290 @Override 291 public PresenceBuilder asBuilder(XMPPConnection connection) { 292 return connection.getStanzaFactory().buildPresenceStanzaFrom(this); 293 } 294 295 @Override 296 public String toString() { 297 StringBuilder sb = new StringBuilder(); 298 sb.append("Presence Stanza ["); 299 logCommonAttributes(sb); 300 sb.append("type=").append(type).append(','); 301 if (mode != null) { 302 sb.append("mode=").append(mode).append(','); 303 } 304 if (!StringUtils.isNullOrEmpty(status)) { 305 sb.append("status=").append(status).append(','); 306 } 307 if (priority != null) { 308 sb.append("prio=").append(priority).append(','); 309 } 310 sb.append(']'); 311 return sb.toString(); 312 } 313 314 @Override 315 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 316 XmlStringBuilder buf = new XmlStringBuilder(this, enclosingNamespace); 317 addCommonAttributes(buf); 318 if (type != Type.available) { 319 buf.attribute("type", type); 320 } 321 322 List<ExtensionElement> extensions = getExtensions(); 323 if (status == null 324 && priority == null 325 && (mode == null || mode == Mode.available) 326 && extensions.isEmpty() 327 && getError() == null) { 328 return buf.closeEmptyElement(); 329 } 330 331 buf.rightAngleBracket(); 332 333 buf.optElement("status", status); 334 buf.optElement("priority", priority); 335 if (mode != null && mode != Mode.available) { 336 buf.element("show", mode); 337 } 338 339 buf.append(extensions); 340 341 // Add the error sub-packet, if there is one. 342 appendErrorIfExists(buf); 343 344 buf.closeElement(ELEMENT); 345 346 return buf; 347 } 348 349 /** 350 * Creates and returns a copy of this presence stanza. 351 * <p> 352 * This does not perform a deep clone, as extension elements are shared between the new and old 353 * instance. 354 * </p> 355 * @return a clone of this presence. 356 * @deprecated use {@link #asBuilder()} instead. 357 */ 358 // TODO: Remove in Smack 4.5. 359 @Deprecated 360 @Override 361 public Presence clone() { 362 return new Presence(this); 363 } 364 365 /** 366 * Clone this presence and set a newly generated stanza ID as the clone's ID. 367 * 368 * @return a "clone" of this presence with a different stanza ID. 369 * @since 4.1.2 370 * @deprecated use {@link #asBuilder(XMPPConnection)} or {@link #asBuilder(String)}instead. 371 */ 372 // TODO: Remove in Smack 4.5. 373 @Deprecated 374 public Presence cloneWithNewId() { 375 Presence clone = clone(); 376 clone.setNewStanzaId(); 377 return clone; 378 } 379 380 /** 381 * An enum to represent the presence type. Note that presence type is often confused 382 * with presence mode. Generally, if a user is signed in to a server, they have a presence 383 * type of {@link #available available}, even if the mode is {@link Mode#away away}, 384 * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when 385 * the user is signing out of the server. 386 */ 387 public enum Type { 388 389 /** 390 * The user is available to receive messages (default). 391 */ 392 available, 393 394 /** 395 * The user is unavailable to receive messages. 396 */ 397 unavailable, 398 399 /** 400 * Request subscription to recipient's presence. 401 */ 402 subscribe, 403 404 /** 405 * Grant subscription to sender's presence. 406 */ 407 subscribed, 408 409 /** 410 * Request removal of subscription to sender's presence. 411 */ 412 unsubscribe, 413 414 /** 415 * Grant removal of subscription to sender's presence. 416 */ 417 unsubscribed, 418 419 /** 420 * The presence stanza contains an error message. 421 */ 422 error, 423 424 /** 425 * A presence probe as defined in section 4.3 of RFC 6121. 426 */ 427 probe, 428 ; 429 430 /** 431 * Converts a String into the corresponding types. Valid String values that can be converted 432 * to types are: "available", "unavailable", "subscribe", "subscribed", "unsubscribe", 433 * "unsubscribed" and "error". 434 * 435 * @param string the String value to covert. 436 * @return the corresponding Type. 437 * @throws IllegalArgumentException when not able to parse the string parameter 438 * @throws NullPointerException if the string is null 439 */ 440 public static Type fromString(String string) { 441 return Type.valueOf(string.toLowerCase(Locale.US)); 442 } 443 } 444 445 /** 446 * An enum to represent the presence mode. 447 */ 448 public enum Mode { 449 450 /** 451 * Free to chat. 452 */ 453 chat, 454 455 /** 456 * Available (the default). 457 */ 458 available, 459 460 /** 461 * Away. 462 */ 463 away, 464 465 /** 466 * Away for an extended period of time. 467 */ 468 xa, 469 470 /** 471 * Do not disturb. 472 */ 473 dnd; 474 475 /** 476 * Converts a String into the corresponding types. Valid String values that can be converted 477 * to types are: "chat", "available", "away", "xa", and "dnd". 478 * 479 * @param string the String value to covert. 480 * @return the corresponding Type. 481 * @throws IllegalArgumentException when not able to parse the string parameter 482 * @throws NullPointerException if the string is null 483 */ 484 public static Mode fromString(String string) { 485 return Mode.valueOf(string.toLowerCase(Locale.US)); 486 } 487 } 488}