001/**
002 *
003 * Copyright 2014 Vyacheslav Blinov
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 */
017package org.jivesoftware.smackx.amp.packet;
018
019import java.util.Collections;
020import java.util.List;
021import java.util.concurrent.CopyOnWriteArrayList;
022
023import org.jivesoftware.smack.packet.ExtensionElement;
024import org.jivesoftware.smackx.amp.AMPDeliverCondition;
025import org.jivesoftware.smackx.amp.AMPExpireAtCondition;
026import org.jivesoftware.smackx.amp.AMPMatchResourceCondition;
027
028public class AMPExtension implements ExtensionElement {
029
030    public static final String NAMESPACE = "http://jabber.org/protocol/amp";
031    public static final String ELEMENT = "amp";
032
033    private CopyOnWriteArrayList<Rule> rules = new CopyOnWriteArrayList<Rule>();
034    private boolean perHop = false;
035
036    private final String from;
037    private final String to;
038    private final Status status;
039
040    /**
041     * Create a new AMPExtension instance with defined from, to and status attributes. Used to create incoming packets.
042     * @param from jid that triggered this amp callback.
043     * @param to receiver of this amp receipt.
044     * @param status status of this amp receipt.
045     */
046    public AMPExtension(String from, String to, Status status) {
047        this.from = from;
048        this.to = to;
049        this.status = status;
050    }
051
052    /**
053     * Create a new amp request extension to be used with outgoing message.
054     */
055    public AMPExtension() {
056        this.from = null;
057        this.to = null;
058        this.status = null;
059    }
060
061    /**
062     * @return jid that triggered this amp callback.
063     */
064    public String getFrom() {
065        return from;
066    }
067
068    /**
069     * @return receiver of this amp receipt.
070     */
071    public String getTo() {
072        return to;
073    }
074
075    /**
076     * Status of this amp notification
077     * @return Status for this amp
078     */
079    public Status getStatus() {
080        return status;
081    }
082
083    /**
084     * Returns a unmodifiable List of the rules in the packet.
085     *
086     * @return a unmodifiable List of the rules in the packet.
087     */
088    public List<Rule> getRules() {
089        return Collections.unmodifiableList(rules);
090    }
091
092    /**
093     * Adds a rule to the amp element. Amp can have any number of rules.
094     *
095     * @param rule the rule to add.
096     */
097    public void addRule(Rule rule) {
098        rules.add(rule);
099    }
100
101    /**
102     * Returns a count of the rules in the AMP packet.
103     *
104     * @return the number of rules in the AMP packet.
105     */
106    public int getRulesCount() {
107        return rules.size();
108    }
109
110    /**
111     * Sets this amp ruleset to be "per-hop".
112     *
113     * @param enabled true if "per-hop" should be enabled
114     */
115    public synchronized void setPerHop(boolean enabled) {
116        perHop = enabled;
117    }
118
119    /**
120     * Returns true is this ruleset is "per-hop".
121     *
122     * @return true is this ruleset is "per-hop".
123     */
124    public synchronized boolean isPerHop() {
125        return perHop;
126    }
127
128    /**
129     * Returns the XML element name of the extension sub-packet root element.
130     * Always returns "amp"
131     *
132     * @return the XML element name of the stanza(/packet) extension.
133     */
134    @Override
135    public String getElementName() {
136        return ELEMENT;
137    }
138
139    /**
140     * Returns the XML namespace of the extension sub-packet root element.
141     * According the specification the namespace is always "http://jabber.org/protocol/xhtml-im"
142     *
143     * @return the XML namespace of the stanza(/packet) extension.
144     */
145    @Override
146    public String getNamespace() {
147        return NAMESPACE;
148    }
149
150    /**
151     * Returns the XML representation of a XHTML extension according the specification.
152     **/
153    @Override
154    public String toXML() {
155        StringBuilder buf = new StringBuilder();
156        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\"");
157        if (status != null) {
158            buf.append(" status=\"").append(status.toString()).append("\"");
159        }
160        if (to != null) {
161            buf.append(" to=\"").append(to).append("\"");
162        }
163        if (from != null) {
164            buf.append(" from=\"").append(from).append("\"");
165        }
166        if (perHop) {
167            buf.append(" per-hop=\"true\"");
168        }
169        buf.append(">");
170
171        // Loop through all the rules and append them to the string buffer
172        for (Rule rule : getRules()) {
173            buf.append(rule.toXML());
174        }
175
176        buf.append("</").append(getElementName()).append(">");
177        return buf.toString();
178    }
179
180    /**
181     * XEP-0079 Rule element. Defines AMP Rule parameters. Can be added to AMPExtension.
182     */
183    public static class Rule {
184        public static final String ELEMENT = "rule";
185
186        private final Action action;
187        private final Condition condition;
188
189        public Action getAction() {
190            return action;
191        }
192
193        public Condition getCondition() {
194            return condition;
195        }
196
197        /**
198         * Create a new amp rule with specified action and condition. Value will be taken from condition argument
199         * @param action action for this rule
200         * @param condition condition for this rule
201         */
202        public Rule(Action action, Condition condition) {
203            if (action == null)
204                throw new NullPointerException("Can't create Rule with null action");
205            if (condition == null)
206                throw new NullPointerException("Can't create Rule with null condition");
207
208            this.action = action;
209            this.condition = condition;
210        }
211
212        private String toXML() {
213            return "<" + ELEMENT + " " + Action.ATTRIBUTE_NAME + "=\"" + action.toString() + "\" " +
214                    Condition.ATTRIBUTE_NAME + "=\"" + condition.getName() + "\" " +
215                    "value=\"" + condition.getValue() + "\"/>";
216        }
217    }
218
219    /**
220     * Interface for defining XEP-0079 Conditions and their values
221     * @see AMPDeliverCondition
222     * @see AMPExpireAtCondition
223     * @see AMPMatchResourceCondition
224     **/
225    public static interface Condition {
226        String getName();
227        String getValue();
228
229        static final String ATTRIBUTE_NAME="condition";
230    }
231
232    /**
233     * amp action attribute
234     * See http://xmpp.org/extensions/xep-0079.html#actions-def
235     **/
236    public static enum Action {
237        /**
238         * The "alert" action triggers a reply <message/> stanza to the sending entity.
239         * This <message/> stanza MUST contain the element <amp status='alert'/>,
240         * which itself contains the <rule/> that triggered this action. In all other respects,
241         * this action behaves as "drop".
242         */
243        alert,
244        /**
245         * The "drop" action silently discards the message from any further delivery attempts
246         * and ensures that it is not placed into offline storage.
247         * The drop MUST NOT result in other responses.
248         */
249        drop,
250        /**
251         * The "error" action triggers a reply <message/> stanza of type "error" to the sending entity.
252         * The <message/> stanza's <error/> child MUST contain a
253         * <failed-rules xmlns='http://jabber.org/protocol/amp#errors'/> error condition,
254         * which itself contains the rules that triggered this action.
255         */
256        error,
257        /**
258         * The "notify" action triggers a reply <message/> stanza to the sending entity.
259         * This <message/> stanza MUST contain the element <amp status='notify'/>, which itself
260         * contains the <rule/> that triggered this action. Unlike the other actions,
261         * this action does not override the default behavior for a server.
262         * Instead, the server then executes its default behavior after sending the notify.
263         */
264        notify;
265
266        public static final String ATTRIBUTE_NAME="action";
267    }
268
269    /**
270     * amp notification status as defined by XEP-0079
271     */
272    public static enum Status {
273        alert,
274        error,
275        notify
276    }
277}