mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-12-23 13:07:59 +01:00
Advanced Message Processing implementation (XEP-0079)
Fixes SMACK-544
This commit is contained in:
parent
06f88674ee
commit
61bf5cd4ce
10 changed files with 878 additions and 0 deletions
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Connection;
|
||||||
|
|
||||||
|
public class AMPDeliverCondition implements AMPExtension.Condition {
|
||||||
|
|
||||||
|
public static final String NAME = "deliver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server supports deliver condition
|
||||||
|
* @param connection Smack connection instance
|
||||||
|
* @return true if deliver condition is supported.
|
||||||
|
*/
|
||||||
|
public static boolean isSupported(Connection connection) {
|
||||||
|
return AMPManager.isConditionSupported(connection, NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Value value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new amp deliver condition with value setted to one of defined by XEP-0079.
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-deliver
|
||||||
|
* @param value AMPDeliveryCondition.Value instance that will be used as value parameter. Can't be null.
|
||||||
|
*/
|
||||||
|
public AMPDeliverCondition(Value value) {
|
||||||
|
if (value == null)
|
||||||
|
throw new NullPointerException("Can't create AMPDeliverCondition with null value");
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value for amp deliver condition as defined by XEP-0079.
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-deliver
|
||||||
|
*/
|
||||||
|
public static enum Value {
|
||||||
|
/**
|
||||||
|
* The message would be immediately delivered to the intended recipient or routed to the next hop.
|
||||||
|
*/
|
||||||
|
direct,
|
||||||
|
/**
|
||||||
|
* The message would be forwarded to another XMPP address or account.
|
||||||
|
*/
|
||||||
|
forward,
|
||||||
|
/**
|
||||||
|
* The message would be sent through a gateway to an address or account on a non-XMPP system.
|
||||||
|
*/
|
||||||
|
gateway,
|
||||||
|
/**
|
||||||
|
* The message would not be delivered at all (e.g., because the intended recipient is offline and message storage is not enabled).
|
||||||
|
*/
|
||||||
|
none,
|
||||||
|
/**
|
||||||
|
* The message would be stored offline for later delivery to the intended recipient.
|
||||||
|
*/
|
||||||
|
stored
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Connection;
|
||||||
|
import org.jivesoftware.smack.util.XmppDateTime;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
|
||||||
|
public class AMPExpireAtCondition implements AMPExtension.Condition {
|
||||||
|
|
||||||
|
public static final String NAME = "expire-at";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server supports expire-at condition
|
||||||
|
* @param connection Smack connection instance
|
||||||
|
* @return true if expire-at condition is supported.
|
||||||
|
*/
|
||||||
|
public static boolean isSupported(Connection connection) {
|
||||||
|
return AMPManager.isConditionSupported(connection, NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new expire-at amp condition with value setted as XEP-0082 formatted date.
|
||||||
|
* @param utcDateTime Date instance of time
|
||||||
|
* that will be used as value parameter after formatting to XEP-0082 format. Can't be null.
|
||||||
|
*/
|
||||||
|
public AMPExpireAtCondition(Date utcDateTime) {
|
||||||
|
if (utcDateTime == null)
|
||||||
|
throw new NullPointerException("Can't create AMPExpireAtCondition with null value");
|
||||||
|
this.value = XmppDateTime.formatXEP0082Date(utcDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new expire-at amp condition with defined value.
|
||||||
|
* @param utcDateTime UTC time string that will be used as value parameter.
|
||||||
|
* Should be formatted as XEP-0082 Date format. Can't be null.
|
||||||
|
*/
|
||||||
|
public AMPExpireAtCondition(String utcDateTime) {
|
||||||
|
if (utcDateTime == null)
|
||||||
|
throw new NullPointerException("Can't create AMPExpireAtCondition with null value");
|
||||||
|
this.value = utcDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,273 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
public class AMPExtension implements PacketExtension {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "http://jabber.org/protocol/amp";
|
||||||
|
public static final String ELEMENT = "amp";
|
||||||
|
|
||||||
|
private CopyOnWriteArrayList<Rule> rules = new CopyOnWriteArrayList<Rule>();
|
||||||
|
private boolean perHop = false;
|
||||||
|
|
||||||
|
private final String from;
|
||||||
|
private final String to;
|
||||||
|
private final Status status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AMPExtension instance with defined from, to and status attributes. Used to create incoming packets.
|
||||||
|
* @param from jid that triggered this amp callback.
|
||||||
|
* @param to receiver of this amp receipt.
|
||||||
|
* @param status status of this amp receipt.
|
||||||
|
*/
|
||||||
|
public AMPExtension(String from, String to, Status status) {
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new amp request extension to be used with outgoing message.
|
||||||
|
*/
|
||||||
|
public AMPExtension() {
|
||||||
|
this.from = null;
|
||||||
|
this.to = null;
|
||||||
|
this.status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return jid that triggered this amp callback.
|
||||||
|
*/
|
||||||
|
public String getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return receiver of this amp receipt.
|
||||||
|
*/
|
||||||
|
public String getTo() {
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status of this amp notification
|
||||||
|
* @return Status for this amp
|
||||||
|
*/
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator for the rules in the packet.
|
||||||
|
*
|
||||||
|
* @return an Iterator for the rules in the packet.
|
||||||
|
*/
|
||||||
|
public Iterator<Rule> getRules() {
|
||||||
|
return Collections.unmodifiableList(new ArrayList<Rule>(rules)).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a rule to the amp element. Amp can have any number of rules.
|
||||||
|
*
|
||||||
|
* @param rule the rule to add.
|
||||||
|
*/
|
||||||
|
public void addRule(Rule rule) {
|
||||||
|
rules.add(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a count of the rules in the AMP packet.
|
||||||
|
*
|
||||||
|
* @return the number of rules in the AMP packet.
|
||||||
|
*/
|
||||||
|
public int getRulesCount() {
|
||||||
|
return rules.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this amp ruleset to be "per-hop".
|
||||||
|
*
|
||||||
|
* @param enabled true if "per-hop" should be enabled
|
||||||
|
*/
|
||||||
|
public synchronized void setPerHop(boolean enabled) {
|
||||||
|
perHop = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true is this ruleset is "per-hop".
|
||||||
|
*
|
||||||
|
* @return true is this ruleset is "per-hop".
|
||||||
|
*/
|
||||||
|
public synchronized boolean isPerHop() {
|
||||||
|
return perHop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XML element name of the extension sub-packet root element.
|
||||||
|
* Always returns "amp"
|
||||||
|
*
|
||||||
|
* @return the XML element name of the packet extension.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XML namespace of the extension sub-packet root element.
|
||||||
|
* According the specification the namespace is always "http://jabber.org/protocol/xhtml-im"
|
||||||
|
*
|
||||||
|
* @return the XML namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XML representation of a XHTML extension according the specification.
|
||||||
|
**/
|
||||||
|
@Override
|
||||||
|
public String toXML() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\"");
|
||||||
|
if (status != null) {
|
||||||
|
buf.append(" status=\"").append(status.toString()).append("\"");
|
||||||
|
}
|
||||||
|
if (to != null) {
|
||||||
|
buf.append(" to=\"").append(to).append("\"");
|
||||||
|
}
|
||||||
|
if (from != null) {
|
||||||
|
buf.append(" from=\"").append(from).append("\"");
|
||||||
|
}
|
||||||
|
if (perHop) {
|
||||||
|
buf.append(" per-hop=\"true\"");
|
||||||
|
}
|
||||||
|
buf.append(">");
|
||||||
|
|
||||||
|
// Loop through all the rules and append them to the string buffer
|
||||||
|
for (Iterator<Rule> i = getRules(); i.hasNext();) {
|
||||||
|
buf.append(i.next().toXML());
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</").append(getElementName()).append(">");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XEP-0079 Rule element. Defines AMP Rule parameters. Can be added to AMPExtension.
|
||||||
|
*/
|
||||||
|
public static class Rule {
|
||||||
|
public static final String ELEMENT = "rule";
|
||||||
|
|
||||||
|
private final Action action;
|
||||||
|
private final Condition condition;
|
||||||
|
|
||||||
|
public Action getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Condition getCondition() {
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new amp rule with specified action and condition. Value will be taken from condition argument
|
||||||
|
* @param action action for this rule
|
||||||
|
* @param condition condition for this rule
|
||||||
|
*/
|
||||||
|
public Rule(Action action, Condition condition) {
|
||||||
|
if (action == null)
|
||||||
|
throw new NullPointerException("Can't create Rule with null action");
|
||||||
|
if (condition == null)
|
||||||
|
throw new NullPointerException("Can't create Rule with null condition");
|
||||||
|
|
||||||
|
this.action = action;
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toXML() {
|
||||||
|
return "<" + ELEMENT + " " + Action.ATTRIBUTE_NAME + "=\"" + action.toString() + "\" " +
|
||||||
|
Condition.ATTRIBUTE_NAME + "=\"" + condition.getName() + "\" " +
|
||||||
|
"value=\"" + condition.getValue() + "\"/>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for defining XEP-0079 Conditions and their values
|
||||||
|
* @see AMPDeliverCondition
|
||||||
|
* @see AMPExpireAtCondition
|
||||||
|
* @see AMPMatchResourceCondition
|
||||||
|
**/
|
||||||
|
public static interface Condition {
|
||||||
|
String getName();
|
||||||
|
String getValue();
|
||||||
|
|
||||||
|
static final String ATTRIBUTE_NAME="condition";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* amp action attribute
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html#actions-def
|
||||||
|
**/
|
||||||
|
public static enum Action {
|
||||||
|
/**
|
||||||
|
* The "alert" action triggers a reply <message/> stanza to the sending entity.
|
||||||
|
* This <message/> stanza MUST contain the element <amp status='alert'/>,
|
||||||
|
* which itself contains the <rule/> that triggered this action. In all other respects,
|
||||||
|
* this action behaves as "drop".
|
||||||
|
*/
|
||||||
|
alert,
|
||||||
|
/**
|
||||||
|
* The "drop" action silently discards the message from any further delivery attempts
|
||||||
|
* and ensures that it is not placed into offline storage.
|
||||||
|
* The drop MUST NOT result in other responses.
|
||||||
|
*/
|
||||||
|
drop,
|
||||||
|
/**
|
||||||
|
* The "error" action triggers a reply <message/> stanza of type "error" to the sending entity.
|
||||||
|
* The <message/> stanza's <error/> child MUST contain a
|
||||||
|
* <failed-rules xmlns='http://jabber.org/protocol/amp#errors'/> error condition,
|
||||||
|
* which itself contains the rules that triggered this action.
|
||||||
|
*/
|
||||||
|
error,
|
||||||
|
/**
|
||||||
|
* The "notify" action triggers a reply <message/> stanza to the sending entity.
|
||||||
|
* This <message/> stanza MUST contain the element <amp status='notify'/>, which itself
|
||||||
|
* contains the <rule/> that triggered this action. Unlike the other actions,
|
||||||
|
* this action does not override the default behavior for a server.
|
||||||
|
* Instead, the server then executes its default behavior after sending the notify.
|
||||||
|
*/
|
||||||
|
notify;
|
||||||
|
|
||||||
|
static final String ATTRIBUTE_NAME="action";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* amp notification status as defined by XEP-0079
|
||||||
|
*/
|
||||||
|
public static enum Status {
|
||||||
|
alert,
|
||||||
|
error,
|
||||||
|
notify
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
|
||||||
|
public class AMPExtensionProvider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMPExtensionProvider.
|
||||||
|
* ProviderManager requires that every PacketExtensionProvider has a public, no-argument constructor
|
||||||
|
*/
|
||||||
|
public AMPExtensionProvider() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a AMPExtension packet (extension sub-packet).
|
||||||
|
*
|
||||||
|
* @param parser the XML parser, positioned at the starting element of the extension.
|
||||||
|
* @return a PacketExtension.
|
||||||
|
* @throws Exception if a parsing error occurs.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||||
|
final String from = parser.getAttributeValue(null, "from");
|
||||||
|
final String to = parser.getAttributeValue(null, "to");
|
||||||
|
final String statusString = parser.getAttributeValue(null, "status");
|
||||||
|
AMPExtension.Status status = null;
|
||||||
|
if (statusString != null) {
|
||||||
|
try {
|
||||||
|
status = AMPExtension.Status.valueOf(statusString);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.err.println("Found invalid amp status " + statusString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMPExtension ampExtension = new AMPExtension(from, to, status);
|
||||||
|
|
||||||
|
String perHopValue = parser.getAttributeValue(null, "per-hop");
|
||||||
|
if (perHopValue != null) {
|
||||||
|
boolean perHop = Boolean.parseBoolean(perHopValue);
|
||||||
|
ampExtension.setPerHop(perHop);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
if (parser.getName().equals(AMPExtension.Rule.ELEMENT)) {
|
||||||
|
String actionString = parser.getAttributeValue(null, AMPExtension.Action.ATTRIBUTE_NAME);
|
||||||
|
String conditionName = parser.getAttributeValue(null, AMPExtension.Condition.ATTRIBUTE_NAME);
|
||||||
|
String conditionValue = parser.getAttributeValue(null, "value");
|
||||||
|
|
||||||
|
AMPExtension.Condition condition = createCondition(conditionName, conditionValue);
|
||||||
|
AMPExtension.Action action = null;
|
||||||
|
if (actionString != null) {
|
||||||
|
try {
|
||||||
|
action = AMPExtension.Action.valueOf(actionString);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.err.println("Found invalid rule action value " + actionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == null || condition == null) {
|
||||||
|
System.err.println("Rule is skipped because either it's action or it's condition is invalid");
|
||||||
|
} else {
|
||||||
|
AMPExtension.Rule rule = new AMPExtension.Rule(action, condition);
|
||||||
|
ampExtension.addRule(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(AMPExtension.ELEMENT)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ampExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AMPExtension.Condition createCondition(String name, String value) {
|
||||||
|
if (name == null || value == null) {
|
||||||
|
System.err.println("Can't create rule condition from null name and/or value");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (AMPDeliverCondition.NAME.equals(name)) {
|
||||||
|
try {
|
||||||
|
return new AMPDeliverCondition(AMPDeliverCondition.Value.valueOf(value));
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.err.println("Found invalid rule delivery condition value " + value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (AMPExpireAtCondition.NAME.equals(name)) {
|
||||||
|
return new AMPExpireAtCondition(value);
|
||||||
|
} else if (AMPMatchResourceCondition.NAME.equals(name)) {
|
||||||
|
try {
|
||||||
|
return new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.valueOf(value));
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.err.println("Found invalid rule match-resource condition value " + value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Found unknown rule condition name " + name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Connection;
|
||||||
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages AMP stanzas within messages. A AMPManager provides a high level access to
|
||||||
|
* get and set AMP rules to messages.
|
||||||
|
*
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html for AMP extension details
|
||||||
|
*
|
||||||
|
* @author Vyacheslav Blinov
|
||||||
|
*/
|
||||||
|
public class AMPManager {
|
||||||
|
|
||||||
|
|
||||||
|
// Enable the AMP support on every established connection
|
||||||
|
// The ServiceDiscoveryManager class should have been already initialized
|
||||||
|
static {
|
||||||
|
Connection.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||||
|
public void connectionCreated(Connection connection) {
|
||||||
|
AMPManager.setServiceEnabled(connection, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables the AMP support on a given connection.<p>
|
||||||
|
*
|
||||||
|
* Before starting to send AMP messages to a user, check that the user can handle XHTML
|
||||||
|
* messages. Enable the AMP support to indicate that this client handles XHTML messages.
|
||||||
|
*
|
||||||
|
* @param connection the connection where the service will be enabled or disabled
|
||||||
|
* @param enabled indicates if the service will be enabled or disabled
|
||||||
|
*/
|
||||||
|
public synchronized static void setServiceEnabled(Connection connection, boolean enabled) {
|
||||||
|
if (isServiceEnabled(connection) == enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(AMPExtension.NAMESPACE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(AMPExtension.NAMESPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the AMP support is enabled for the given connection.
|
||||||
|
*
|
||||||
|
* @param connection the connection to look for AMP support
|
||||||
|
* @return a boolean indicating if the AMP support is enabled for the given connection
|
||||||
|
*/
|
||||||
|
public static boolean isServiceEnabled(Connection connection) {
|
||||||
|
connection.getServiceName();
|
||||||
|
return ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(AMPExtension.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server supports specified action
|
||||||
|
* @param connection active xmpp connection
|
||||||
|
* @param action action to check
|
||||||
|
* @return true if this action is supported.
|
||||||
|
*/
|
||||||
|
public static boolean isActionSupported(Connection connection, AMPExtension.Action action) {
|
||||||
|
String featureName = AMPExtension.NAMESPACE + "?action=" + action.toString();
|
||||||
|
return isFeatureSupportedByServer(connection, featureName, AMPExtension.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server supports specified condition
|
||||||
|
* @param connection active xmpp connection
|
||||||
|
* @param conditionName name of condition to check
|
||||||
|
* @return true if this condition is supported.
|
||||||
|
* @see AMPDeliverCondition
|
||||||
|
* @see AMPExpireAtCondition
|
||||||
|
* @see AMPMatchResourceCondition
|
||||||
|
*/
|
||||||
|
public static boolean isConditionSupported(Connection connection, String conditionName) {
|
||||||
|
String featureName = AMPExtension.NAMESPACE + "?condition=" + conditionName;
|
||||||
|
return isFeatureSupportedByServer(connection, featureName, AMPExtension.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFeatureSupportedByServer(Connection connection, String featureName, String node) {
|
||||||
|
try {
|
||||||
|
ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
|
DiscoverInfo info = discoveryManager.discoverInfo(connection.getServiceName(), node);
|
||||||
|
Iterator<DiscoverInfo.Feature> it = info.getFeatures();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
DiscoverInfo.Feature feature = it.next();
|
||||||
|
if (featureName.equals(feature.getVar())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XMPPException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Connection;
|
||||||
|
|
||||||
|
public class AMPMatchResourceCondition implements AMPExtension.Condition {
|
||||||
|
|
||||||
|
public static final String NAME = "match-resource";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if server supports match-resource condition
|
||||||
|
* @param connection Smack connection instance
|
||||||
|
* @return true if match-resource condition is supported.
|
||||||
|
*/
|
||||||
|
public static boolean isSupported(Connection connection) {
|
||||||
|
return AMPManager.isConditionSupported(connection, NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Value value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new amp match-resource condition with value setted to one of defined by XEP-0079.
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-match
|
||||||
|
* @param value AMPDeliveryCondition.Value instance that will be used as value parameter. Can't be null.
|
||||||
|
*/
|
||||||
|
public AMPMatchResourceCondition(Value value) {
|
||||||
|
if (value == null)
|
||||||
|
throw new NullPointerException("Can't create AMPMatchResourceCondition with null value");
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* match-resource amp condition value as defined by XEP-0079
|
||||||
|
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-match
|
||||||
|
*/
|
||||||
|
public static enum Value {
|
||||||
|
/**
|
||||||
|
* Destination resource matches any value, effectively ignoring the intended resource.
|
||||||
|
* Example: "home/laptop" matches "home", "home/desktop" or "work/desktop"
|
||||||
|
*/
|
||||||
|
any,
|
||||||
|
/**
|
||||||
|
* Destination resource exactly matches the intended resource.
|
||||||
|
* Example: "home/laptop" only matches "home/laptop" and not "home/desktop" or "work/desktop"
|
||||||
|
*/
|
||||||
|
exact,
|
||||||
|
/**
|
||||||
|
* Destination resource matches any value except for the intended resource.
|
||||||
|
* Example: "home/laptop" matches "work/desktop", "home" or "home/desktop", but not "home/laptop"
|
||||||
|
*/
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
|
@ -449,4 +449,11 @@
|
||||||
<className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className>
|
<className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className>
|
||||||
</iqProvider>
|
</iqProvider>
|
||||||
|
|
||||||
|
<!-- XEP-0079 Advanced Message Processing -->
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>amp</elementName>
|
||||||
|
<namespace>http://jabber.org/protocol/amp</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.amp.AMPExtensionProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
|
||||||
</smackProviders>
|
</smackProviders>
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2014 Vyacheslav Blinov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.amp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class AMPExtensionTest {
|
||||||
|
|
||||||
|
private InputStream CORRECT_SENDING_STANZA_STREAM;
|
||||||
|
private InputStream INCORRECT_RECEIVING_STANZA_STREAM;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
CORRECT_SENDING_STANZA_STREAM = getClass().getResourceAsStream("correct_stanza_test.xml");
|
||||||
|
INCORRECT_RECEIVING_STANZA_STREAM = getClass().getResourceAsStream("incorrect_stanza_test.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCorrectToXmlTransform() throws IOException {
|
||||||
|
String correctStanza = toString(CORRECT_SENDING_STANZA_STREAM);
|
||||||
|
|
||||||
|
AMPExtension ext = new AMPExtension();
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.alert, new AMPDeliverCondition(AMPDeliverCondition.Value.direct)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.drop, new AMPDeliverCondition(AMPDeliverCondition.Value.forward)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.error, new AMPDeliverCondition(AMPDeliverCondition.Value.gateway)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPDeliverCondition(AMPDeliverCondition.Value.none)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPDeliverCondition(AMPDeliverCondition.Value.stored)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPExpireAtCondition("2004-09-10T08:33:14Z")));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.any)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.exact)));
|
||||||
|
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.other)));
|
||||||
|
|
||||||
|
assertEquals(correctStanza, ext.toXML());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCorrectFromXmlErrorHandling() throws Exception {
|
||||||
|
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
||||||
|
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||||
|
parser.setInput(INCORRECT_RECEIVING_STANZA_STREAM, "UTF-8");
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.START_TAG, parser.next());
|
||||||
|
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
||||||
|
|
||||||
|
PacketExtension extension = ampProvider.parseExtension(parser);
|
||||||
|
assertTrue(extension instanceof AMPExtension);
|
||||||
|
AMPExtension amp = (AMPExtension) extension;
|
||||||
|
|
||||||
|
assertEquals(0, amp.getRulesCount());
|
||||||
|
assertEquals(AMPExtension.Status.alert, amp.getStatus());
|
||||||
|
assertEquals("bernardo@hamlet.lit/elsinore", amp.getFrom());
|
||||||
|
assertEquals("francisco@hamlet.lit", amp.getTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCorrectFromXmlDeserialization() throws Exception {
|
||||||
|
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
||||||
|
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||||
|
parser.setInput(CORRECT_SENDING_STANZA_STREAM, "UTF-8");
|
||||||
|
|
||||||
|
assertEquals(XmlPullParser.START_TAG, parser.next());
|
||||||
|
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
||||||
|
PacketExtension extension = ampProvider.parseExtension(parser);
|
||||||
|
assertTrue(extension instanceof AMPExtension);
|
||||||
|
AMPExtension amp = (AMPExtension) extension;
|
||||||
|
|
||||||
|
assertEquals(9, amp.getRulesCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String toString(InputStream stream) throws IOException {
|
||||||
|
byte[] data = new byte[stream.available()];
|
||||||
|
stream.read(data);
|
||||||
|
stream.close();
|
||||||
|
|
||||||
|
return new String(data, Charset.defaultCharset());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<amp xmlns="http://jabber.org/protocol/amp"><rule action="alert" condition="deliver" value="direct"/><rule action="drop" condition="deliver" value="forward"/><rule action="error" condition="deliver" value="gateway"/><rule action="notify" condition="deliver" value="none"/><rule action="notify" condition="deliver" value="stored"/><rule action="notify" condition="expire-at" value="2004-09-10T08:33:14Z"/><rule action="notify" condition="match-resource" value="any"/><rule action="notify" condition="match-resource" value="exact"/><rule action="notify" condition="match-resource" value="other"/></amp>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<amp xmlns='http://jabber.org/protocol/amp'
|
||||||
|
status='alert'
|
||||||
|
from='bernardo@hamlet.lit/elsinore'
|
||||||
|
to='francisco@hamlet.lit'>
|
||||||
|
<rule action='test' condition='deliver' value='direct'/>
|
||||||
|
<rule action='drop' condition='deliver' value='forwarded'/>
|
||||||
|
<rule action='error' condition='delivered' value='gateway'/>
|
||||||
|
<rule action='notify' value='none'/>
|
||||||
|
<rule action='notify' condition='deliver'/>
|
||||||
|
<rule condition='expire-at' value='2004-09-10T08:33:14Z'/>
|
||||||
|
<rule condition='expire-at' />
|
||||||
|
<rule action='notify' condition='match-resource' value='anys'/>
|
||||||
|
</amp>
|
Loading…
Reference in a new issue