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 &amp;&amp; (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}