From 35ac2281258a4b5e91bb71e598d3a98694ea7dd2 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 23 Jan 2017 23:53:55 +0100 Subject: [PATCH] Add CarbonCopyReceivedListener --- .../carbons/CarbonCopyReceivedListener.java | 33 ++++++++ .../smackx/carbons/CarbonManager.java | 83 +++++++++++++++++-- 2 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonCopyReceivedListener.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonCopyReceivedListener.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonCopyReceivedListener.java new file mode 100644 index 000000000..25a82be4b --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonCopyReceivedListener.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2017 Florian Schmaus + * + * 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.carbons; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction; + +public interface CarbonCopyReceivedListener { + + /** + * Invoked when a new carbon copy was received. + * + * @param direction the direction of the carbon copy. + * @param carbonCopy the carbon copy itself. + * @param wrappingMessage the message wrapping the carbon copy. + */ + void onCarbonCopyReceived(Direction direction, Message carbonCopy, Message wrappingMessage); + +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java index b3aad0277..26af1b1d0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/CarbonManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2014 Georg Lukas + * Copyright 2013-2014 Georg Lukas, 2017 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,9 @@ package org.jivesoftware.smackx.carbons; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import org.jivesoftware.smack.ExceptionCallback; import org.jivesoftware.smack.AbstractConnectionListener; @@ -31,23 +33,37 @@ import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.FromMatchesFilter; +import org.jivesoftware.smack.filter.OrFilter; +import org.jivesoftware.smack.filter.StanzaExtensionFilter; +import org.jivesoftware.smack.filter.StanzaFilter; +import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.carbons.packet.Carbon; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Direction; import org.jivesoftware.smackx.carbons.packet.CarbonExtension.Private; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.forward.packet.Forwarded; /** - * Stanza(/Packet) extension for XEP-0280: Message Carbons. This class implements - * the manager for registering {@link CarbonExtension} support, enabling and disabling - * message carbons. - * - * You should call enableCarbons() before sending your first undirected - * presence. + * Manager for XEP-0280: Message Carbons. This class implements the manager for registering {@link CarbonExtension} + * support, enabling and disabling message carbons, and for {@link CarbonCopyReceivedListener}. + *

+ * Note that it is important to match the 'from' attribute of the message wrapping a carbon copy, as otherwise it would + * may be possible for others to impersonate users. Smack's CarbonManager takes care of that in + * {@link CarbonCopyReceivedListener}s which where registered with + * {@link #addCarbonCopyReceivedListener(CarbonCopyReceivedListener)}. + *

+ *

+ * You should call enableCarbons() before sending your first undirected presence (aka. the "initial presence"). + *

* * @author Georg Lukas + * @author Florian Schmaus */ public final class CarbonManager extends Manager { @@ -61,6 +77,19 @@ public final class CarbonManager extends Manager { }); } + private static final StanzaFilter CARBON_EXTENSION_FILTER = + // @formatter:off + new AndFilter( + new OrFilter( + new StanzaExtensionFilter(CarbonExtension.Direction.sent.name(), CarbonExtension.NAMESPACE), + new StanzaExtensionFilter(CarbonExtension.Direction.received.name(), CarbonExtension.NAMESPACE) + ), + StanzaTypeFilter.MESSAGE + ); + // @formatter:on + + private final Set listeners = new CopyOnWriteArraySet<>(); + private volatile boolean enabled_state = false; private CarbonManager(XMPPConnection connection) { @@ -83,6 +112,24 @@ public final class CarbonManager extends Manager { } } }); + + connection.addSyncStanzaListener(new StanzaListener() { + @Override + public void processStanza(final Stanza stanza) throws NotConnectedException, InterruptedException { + final Message wrappingMessage = (Message) stanza; + final CarbonExtension carbonExtension = CarbonExtension.from(wrappingMessage); + final Direction direction = carbonExtension.getDirection(); + final Forwarded forwarded = carbonExtension.getForwarded(); + final Message carbonCopy = (Message) forwarded.getForwardedStanza(); + for (CarbonCopyReceivedListener listener : listeners) { + listener.onCarbonCopyReceived(direction, carbonCopy, wrappingMessage); + } + } + // XEP-0280 ยง 11. Security Considerations "Any forwarded copies received by a Carbons-enabled client MUST be + // from that user's bare JID; any copies that do not meet this requirement MUST be ignored." Otherwise, if + // those copies do not get ignored, malicious users may be able to impersonate other users. That is why the + // 'from' matcher is important here. + }, new AndFilter(CARBON_EXTENSION_FILTER, FromMatchesFilter.createBare(connection.getUser()))); } /** @@ -113,6 +160,28 @@ public final class CarbonManager extends Manager { return request; } + /** + * Add a carbon copy received listener. + * + * @param listener the listener to register. + * @return true if the filter was not already registered. + * @since 4.2 + */ + public boolean addCarbonCopyReceivedListener(CarbonCopyReceivedListener listener) { + return listeners.add(listener); + } + + /** + * Remove a carbon copy received listener. + * + * @param listener the listener to register. + * @return true if the filter was registered. + * @since 4.2 + */ + public boolean removeCarbonCopyReceivedListener(CarbonCopyReceivedListener listener) { + return listeners.remove(listener); + } + /** * Returns true if XMPP Carbons are supported by the server. *