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