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