001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2020-2024 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.StringUtils; 025import org.jivesoftware.smack.util.XmlStringBuilder; 026 027/** 028 * Represents XMPP presence stanzas. Every presence stanza has a type, which is one of 029 * the following values: 030 * <ul> 031 * <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to 032 * receive messages. 033 * <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages. 034 * <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence. 035 * <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence. 036 * <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to 037 * sender's presence. 038 * <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to 039 * sender's presence. 040 * <li>{@link Presence.Type#error error} -- the presence stanza contains an error message. 041 * </ul><p> 042 * 043 * A number of attributes are optional: 044 * <ul> 045 * <li>Status -- free-form text describing a user's presence (i.e., gone to lunch). 046 * <li>Priority -- non-negative numerical priority of a sender's resource. The 047 * highest resource priority is the default recipient of packets not addressed 048 * to a particular resource. 049 * <li>Mode -- one of five presence modes: {@link Mode#available available} (the default), 050 * {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and 051 * {@link Mode#dnd dnd} (do not disturb). 052 * </ul><p> 053 * 054 * Presence stanzas are used for two purposes. First, to notify the server of 055 * the user's current presence status. Second, they are used to subscribe and 056 * unsubscribe users from the roster. 057 * 058 * @author Matt Tucker 059 */ 060public final class Presence extends MessageOrPresence<PresenceBuilder> 061 implements PresenceView { 062 063 public static final String ELEMENT = "presence"; 064 065 private Type type = Type.available; 066 private String status = null; 067 068 /** 069 * The priority of the presence. It is <code>null</code> to indicate that the original 070 * presence stanza did not had an explicit priority set. In which case the priority defaults to 0. 071 * 072 * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3.</a> 073 */ 074 private Byte priority; 075 076 private Mode mode = null; 077 078 Presence(PresenceBuilder presenceBuilder) { 079 super(presenceBuilder); 080 type = presenceBuilder.type; 081 status = presenceBuilder.status; 082 priority = presenceBuilder.priority; 083 mode = presenceBuilder.mode; 084 } 085 086 /** 087 * Copy constructor. 088 * <p> 089 * This does not perform a deep clone, as extension elements are shared between the new and old 090 * instance. 091 * </p> 092 * 093 * @param other TODO javadoc me please 094 */ 095 public Presence(Presence other) { 096 super(other); 097 this.type = other.type; 098 this.status = other.status; 099 this.priority = other.priority; 100 this.mode = other.mode; 101 } 102 103 /** 104 * Returns true if the {@link Type presence type} is available (online) and 105 * false if the user is unavailable (offline), or if this is a presence packet 106 * involved in a subscription operation. This is a convenience method 107 * equivalent to <code>getType() == Presence.Type.available</code>. Note that even 108 * when the user is available, their presence mode may be {@link Mode#away away}, 109 * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use 110 * {@link #isAway()} to determine if the user is away. 111 * 112 * @return true if the presence type is available. 113 */ 114 public boolean isAvailable() { 115 return type == Type.available; 116 } 117 118 /** 119 * Returns true if the presence type is {@link Type#available available} and the presence 120 * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or 121 * {@link Mode#dnd do not disturb}. False will be returned when the type or mode 122 * is any other value, including when the presence type is unavailable (offline). 123 * This is a convenience method equivalent to 124 * <code>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</code>. 125 * 126 * @return true if the presence type is available and the presence mode is away, xa, or dnd. 127 */ 128 public boolean isAway() { 129 return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd); 130 } 131 132 @Override 133 public Type getType() { 134 return type; 135 } 136 137 @Override 138 public String getStatus() { 139 return status; 140 } 141 142 @Override 143 public int getPriority() { 144 return getPriorityByte(); 145 } 146 147 @Override 148 public byte getPriorityByte() { 149 if (priority == null) { 150 return 0; 151 } 152 return priority; 153 } 154 155 /** 156 * Sets the priority of the presence. The valid range is -128 through 127. 157 * 158 * @param priority the priority of the presence. 159 * @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a> 160 * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. 161 */ 162 @Deprecated 163 // TODO: Remove in Smack 4.6. 164 public void setPriority(byte priority) { 165 this.priority = priority; 166 } 167 168 @Override 169 public Mode getMode() { 170 if (mode == null) { 171 return Mode.available; 172 } 173 return mode; 174 } 175 176 @Override 177 public String getElementName() { 178 return ELEMENT; 179 } 180 181 @Override 182 public PresenceBuilder asBuilder() { 183 return StanzaBuilder.buildPresenceFrom(this, getStanzaId()); 184 } 185 186 @Override 187 public PresenceBuilder asBuilder(String id) { 188 return StanzaBuilder.buildPresenceFrom(this, id); 189 } 190 191 @Override 192 public PresenceBuilder asBuilder(XMPPConnection connection) { 193 return connection.getStanzaFactory().buildPresenceStanzaFrom(this); 194 } 195 196 @Override 197 public String toString() { 198 StringBuilder sb = new StringBuilder(); 199 sb.append("Presence Stanza ["); 200 logCommonAttributes(sb); 201 sb.append("type=").append(type).append(','); 202 if (mode != null) { 203 sb.append("mode=").append(mode).append(','); 204 } 205 if (!StringUtils.isNullOrEmpty(status)) { 206 sb.append("status=").append(status).append(','); 207 } 208 if (priority != null) { 209 sb.append("prio=").append(priority).append(','); 210 } 211 sb.append(']'); 212 return sb.toString(); 213 } 214 215 @Override 216 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 217 XmlStringBuilder buf = new XmlStringBuilder(this, enclosingNamespace); 218 addCommonAttributes(buf); 219 if (type != Type.available) { 220 buf.attribute("type", type); 221 } 222 223 List<XmlElement> extensions = getExtensions(); 224 if (status == null 225 && priority == null 226 && (mode == null || mode == Mode.available) 227 && extensions.isEmpty() 228 && getError() == null) { 229 return buf.closeEmptyElement(); 230 } 231 232 buf.rightAngleBracket(); 233 234 buf.optElement("status", status); 235 buf.optElement("priority", priority); 236 if (mode != null && mode != Mode.available) { 237 buf.element("show", mode); 238 } 239 240 buf.append(extensions); 241 242 // Add the error sub-packet, if there is one. 243 appendErrorIfExists(buf); 244 245 buf.closeElement(ELEMENT); 246 247 return buf; 248 } 249 250 /** 251 * An enum to represent the presence type. Note that presence type is often confused 252 * with presence mode. Generally, if a user is signed in to a server, they have a presence 253 * type of {@link #available available}, even if the mode is {@link Mode#away away}, 254 * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when 255 * the user is signing out of the server. 256 */ 257 public enum Type { 258 259 /** 260 * The user is available to receive messages (default). 261 */ 262 available, 263 264 /** 265 * The user is unavailable to receive messages. 266 */ 267 unavailable, 268 269 /** 270 * Request subscription to recipient's presence. 271 */ 272 subscribe, 273 274 /** 275 * Grant subscription to sender's presence. 276 */ 277 subscribed, 278 279 /** 280 * Request removal of subscription to sender's presence. 281 */ 282 unsubscribe, 283 284 /** 285 * Grant removal of subscription to sender's presence. 286 */ 287 unsubscribed, 288 289 /** 290 * The presence stanza contains an error message. 291 */ 292 error, 293 294 /** 295 * A presence probe as defined in section 4.3 of RFC 6121. 296 */ 297 probe, 298 ; 299 300 /** 301 * Converts a String into the corresponding types. Valid String values that can be converted 302 * to types are: "available", "unavailable", "subscribe", "subscribed", "unsubscribe", 303 * "unsubscribed" and "error". 304 * 305 * @param string the String value to covert. 306 * @return the corresponding Type. 307 * @throws IllegalArgumentException when not able to parse the string parameter 308 * @throws NullPointerException if the string is null 309 */ 310 public static Type fromString(String string) { 311 return Type.valueOf(string.toLowerCase(Locale.US)); 312 } 313 } 314 315 /** 316 * An enum to represent the presence mode. 317 */ 318 public enum Mode { 319 320 /** 321 * Free to chat. 322 */ 323 chat, 324 325 /** 326 * Available (the default). 327 */ 328 available, 329 330 /** 331 * Away. 332 */ 333 away, 334 335 /** 336 * Away for an extended period of time. 337 */ 338 xa, 339 340 /** 341 * Do not disturb. 342 */ 343 dnd; 344 345 /** 346 * Converts a String into the corresponding types. Valid String values that can be converted 347 * to types are: "chat", "available", "away", "xa", and "dnd". 348 * 349 * @param string the String value to covert. 350 * @return the corresponding Type. 351 * @throws IllegalArgumentException when not able to parse the string parameter 352 * @throws NullPointerException if the string is null 353 */ 354 public static Mode fromString(String string) { 355 return Mode.valueOf(string.toLowerCase(Locale.US)); 356 } 357 } 358}