diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java index 88342de34..c7eaa5275 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java @@ -35,6 +35,8 @@ public class MessageTypeFilter extends FlexiblePacketTypeFilter { public static final PacketFilter HEADLINE = new MessageTypeFilter(Type.headline); public static final PacketFilter ERROR = new MessageTypeFilter(Type.error); public static final PacketFilter NORMAL_OR_CHAT = new OrFilter(NORMAL, CHAT); + public static final PacketFilter NORMAL_OR_CHAT_OR_HEADLINE = new OrFilter(NORMAL_OR_CHAT, + HEADLINE); private final Message.Type type; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java index 3fa48fdec..b278257eb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceipt.java @@ -22,6 +22,7 @@ import java.util.Map; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.provider.EmbeddedExtensionProvider; +import org.jivesoftware.smack.util.XmlStringBuilder; /** * Represents a message delivery receipt entry as specified by @@ -34,7 +35,10 @@ public class DeliveryReceipt implements PacketExtension public static final String NAMESPACE = "urn:xmpp:receipts"; public static final String ELEMENT = "received"; - private String id; /// original ID of the delivered message + /** + * original ID of the delivered message + */ + private final String id; public DeliveryReceipt(String id) { @@ -59,9 +63,12 @@ public class DeliveryReceipt implements PacketExtension } @Override - public String toXML() + public XmlStringBuilder toXML() { - return ""; + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.attribute("id", id); + xml.closeEmptyElement(); + return xml; } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java index 6693afff1..934827e6a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2014 Georg Lukas + * Copyright 2013-2014 Georg Lukas, 2015 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.filter.PacketExtensionFilter; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; @@ -42,6 +43,24 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; * the manager for {@link DeliveryReceipt} support, enabling and disabling of * automatic DeliveryReceipt transmission. * + *

+ * You can send delivery receipt requests and listen for incoming delivery receipts as shown in this example: + *

+ *
+ * deliveryReceiptManager.addReceiptReceivedListener(new ReceiptReceivedListener() {
+ *   void onReceiptReceived(String fromJid, String toJid, String receiptId, Packet receipt) {
+ *     // If the receiving entity does not support delivery receipts,
+ *     // then the receipt received listener may not get invoked.
+ *   }
+ * });
+ * Message message = …
+ * DeliveryReceiptRequest.addTo(message);
+ * connection.sendPacket(message);
+ * 
+ * + * DeliveryReceiptManager can be configured to automatically add delivery receipt requests to every + * message with {@link #autoAddDeliveryReceiptRequests()}. + * * @author Georg Lukas * @see XEP-0184: Message Delivery Receipts */ @@ -62,7 +81,43 @@ public class DeliveryReceiptManager extends Manager { }); } - private boolean auto_receipts_enabled = false; + /** + * Specifies when incoming message delivery receipt requests should be automatically + * acknowledged with an receipt. + */ + public enum AutoReceiptMode { + + /** + * Never send deliver receipts + */ + disabled, + + /** + * Only send delivery receipts if the requester is subscribed to our presence. + */ + ifIsSubscribed, + + /** + * Always send delivery receipts. Warning: this may causes presence leaks. See XEP-0184: Message Delivery + * Receipts § 8. Security Considerations + */ + always, + } + + private static AutoReceiptMode defaultAutoReceiptMode = AutoReceiptMode.ifIsSubscribed; + + /** + * Set the default automatic receipt mode for new connections. + * + * @param autoReceiptMode the default automatic receipt mode. + */ + public static void setDefaultAutoReceiptMode(AutoReceiptMode autoReceiptMode) { + defaultAutoReceiptMode = autoReceiptMode; + } + + private AutoReceiptMode autoReceiptMode = defaultAutoReceiptMode; + private final Set receiptReceivedListeners = new CopyOnWriteArraySet(); private DeliveryReceiptManager(XMPPConnection connection) { @@ -86,13 +141,23 @@ public class DeliveryReceiptManager extends Manager { connection.addAsyncPacketListener(new PacketListener() { @Override public void processPacket(Packet packet) throws NotConnectedException { - // if enabled, automatically send a receipt - if (!auto_receipts_enabled) { + final String from = packet.getFrom(); + final XMPPConnection connection = connection(); + switch (autoReceiptMode) { + case disabled: return; + case ifIsSubscribed: + if (!connection.getRoster().isSubscribedToMyPresence(from)) { + return; + } + break; + case always: + break; } - Message ack = new Message(packet.getFrom(), Message.Type.normal); + + Message ack = new Message(from, Message.Type.normal); ack.addExtension(new DeliveryReceipt(packet.getPacketID())); - connection().sendPacket(ack); + connection.sendPacket(ack); } }, MESSAGES_WITH_DEVLIERY_RECEIPT_REQUEST); } @@ -130,34 +195,22 @@ public class DeliveryReceiptManager extends Manager { /** * Configure whether the {@link DeliveryReceiptManager} should automatically - * reply to incoming {@link DeliveryReceipt}s. By default, this feature is off. + * reply to incoming {@link DeliveryReceipt}s. * - * @param new_state whether automatic transmission of - * DeliveryReceipts should be enabled or disabled + * @param autoReceiptMode the new auto receipt mode. + * @see AutoReceiptMode */ - public void setAutoReceiptsEnabled(boolean new_state) { - auto_receipts_enabled = new_state; + public void setAutoReceiptMode(AutoReceiptMode autoReceiptMode) { + this.autoReceiptMode = autoReceiptMode; } /** - * Helper method to enable automatic DeliveryReceipt transmission. + * Get the currently active auto receipt mode. + * + * @return the currently active auto receipt mode. */ - public void enableAutoReceipts() { - setAutoReceiptsEnabled(true); - } - - /** - * Helper method to disable automatic DeliveryReceipt transmission. - */ - public void disableAutoReceipts() { - setAutoReceiptsEnabled(false); - } - - /** - * Check if AutoReceipts are enabled on this connection. - */ - public boolean getAutoReceiptsEnabled() { - return this.auto_receipts_enabled; + public AutoReceiptMode getAutoReceiptMode() { + return autoReceiptMode; } /** @@ -178,6 +231,35 @@ public class DeliveryReceiptManager extends Manager { receiptReceivedListeners.remove(listener); } + private static final PacketListener AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER = new PacketListener() { + @Override + public void processPacket(Packet packet) throws NotConnectedException { + Message message = (Message) packet; + DeliveryReceiptRequest.addTo(message); + } + }; + + /** + * Enables automatic requests of delivery receipts for outgoing messages of type 'normal', 'chat' or 'headline. + * + * @since 4.1 + * @see #dontAutoAddDeliveryReceiptRequests() + */ + public void autoAddDeliveryReceiptRequests() { + connection().addPacketSendingListener(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER, + MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE); + } + + /** + * Disables automatically requests of delivery receipts for outgoing messages. + * + * @since 4.1 + * @see #autoAddDeliveryReceiptRequests() + */ + public void dontAutoAddDeliveryReceiptRequests() { + connection().removePacketSendingListener(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER); + } + /** * Test if a message requires a delivery receipt. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java index 612be4575..a3a29ad0d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.packet.id.StanzaIdUtil; import org.jivesoftware.smack.provider.PacketExtensionProvider; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -82,6 +83,9 @@ public class DeliveryReceiptRequest implements PacketExtension * @return the Message ID which will be used as receipt ID */ public static String addTo(Message message) { + if (message.getPacketID() == null) { + message.setPacketID(StanzaIdUtil.newStanzaId()); + } message.addExtension(new DeliveryReceiptRequest()); return message.getPacketID(); } diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml index 6974562fb..b6e64c901 100644 --- a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml +++ b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.xml @@ -15,5 +15,6 @@ org.jivesoftware.smackx.xdata.XDataManager org.jivesoftware.smackx.xdatalayout.XDataLayoutManager org.jivesoftware.smackx.xdatavalidation.XDataValidationManager + org.jivesoftware.smackx.receipts.DeliveryReceiptManager diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java index 10cc98852..3eb1a3ef6 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java @@ -29,6 +29,7 @@ import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.test.util.WaitForPacketListener; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smackx.InitExtensions; +import org.jivesoftware.smackx.receipts.DeliveryReceiptManager.AutoReceiptMode; import org.junit.Test; import org.xmlpull.v1.XmlPullParser; @@ -101,8 +102,8 @@ public class DeliveryReceiptTest extends InitExtensions { c.connect(); DeliveryReceiptManager drm = DeliveryReceiptManager.getInstanceFor(c); - drm.enableAutoReceipts(); - assertTrue(drm.getAutoReceiptsEnabled()); + drm.setAutoReceiptMode(AutoReceiptMode.always); + assertEquals(AutoReceiptMode.always, drm.getAutoReceiptMode()); // test auto-receipts Message m = new Message("julia@capulet.com", Message.Type.normal);