StableUniqueStanzaIdManager.java

  1. /**
  2.  *
  3.  * Copyright 2018 Paul Schaub, 2020 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.sid;

  18. import java.util.Map;
  19. import java.util.WeakHashMap;

  20. import org.jivesoftware.smack.ConnectionCreationListener;
  21. import org.jivesoftware.smack.Manager;
  22. import org.jivesoftware.smack.XMPPConnection;
  23. import org.jivesoftware.smack.XMPPConnectionRegistry;
  24. import org.jivesoftware.smack.filter.AndFilter;
  25. import org.jivesoftware.smack.filter.MessageTypeFilter;
  26. import org.jivesoftware.smack.filter.NotFilter;
  27. import org.jivesoftware.smack.filter.StanzaExtensionFilter;
  28. import org.jivesoftware.smack.filter.StanzaFilter;
  29. import org.jivesoftware.smack.filter.ToTypeFilter;

  30. import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
  31. import org.jivesoftware.smackx.muc.MultiUserChatManager;
  32. import org.jivesoftware.smackx.sid.element.OriginIdElement;

  33. /**
  34.  * Manager class for Stable and Unique Stanza IDs.
  35.  *
  36.  * In order to start automatically appending origin ids to outgoing messages, use {@link #enable()}.
  37.  * This will announce support via the {@link ServiceDiscoveryManager}. If you want to stop appending origin-ids
  38.  * and de-announce support, call {@link #disable()}.
  39.  *
  40.  * @see <a href="https://xmpp.org/extensions/xep-0359.html">XEP-0359: Stable and Unique Stanza IDs</a>
  41.  */
  42. public final class StableUniqueStanzaIdManager extends Manager {

  43.     public static final String NAMESPACE = "urn:xmpp:sid:0";

  44.     private static final Map<XMPPConnection, StableUniqueStanzaIdManager> INSTANCES = new WeakHashMap<>();

  45.     private static boolean enabledByDefault = false;

  46.     // Filter for outgoing stanzas.
  47.     private static final StanzaFilter OUTGOING_FILTER = new AndFilter(
  48.             MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
  49.             ToTypeFilter.ENTITY_FULL_OR_BARE_JID);

  50.     // Filter that filters for messages with an origin id
  51.     private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);

  52.     // We need a filter for outgoing messages that do not carry an origin-id already.
  53.     private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER));

  54.     static {
  55.         XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
  56.             @Override
  57.             public void connectionCreated(XMPPConnection connection) {
  58.                 if (enabledByDefault) {
  59.                     getInstanceFor(connection).enable();
  60.                 }

  61.                 MultiUserChatManager.addDefaultMessageInterceptor((mb, muc) -> {
  62.                     // No need to add an <origin-id/> if the MUC service supports stable IDs.
  63.                     if (muc.serviceSupportsStableIds()) {
  64.                         return;
  65.                     }
  66.                     OriginIdElement.addTo(mb);
  67.                 });
  68.             }
  69.         });
  70.     }

  71.     /**
  72.      * Private constructor.
  73.      * @param connection XMPP connection
  74.      */
  75.     private StableUniqueStanzaIdManager(XMPPConnection connection) {
  76.         super(connection);
  77.     }

  78.     public static void setEnabledByDefault(boolean enabled) {
  79.         enabledByDefault = enabled;
  80.     }

  81.     /**
  82.      * Return an instance of the StableUniqueStanzaIdManager for the given connection.
  83.      *
  84.      * @param connection xmpp-connection
  85.      * @return manager instance for the connection
  86.      */
  87.     public static synchronized StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
  88.         StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
  89.         if (manager == null) {
  90.             manager = new StableUniqueStanzaIdManager(connection);
  91.             INSTANCES.put(connection, manager);
  92.         }
  93.         return manager;
  94.     }

  95.     /**
  96.      * Start appending origin-id elements to outgoing stanzas and add the feature to disco.
  97.      */
  98.     public synchronized void enable() {
  99.         connection().addMessageInterceptor(OriginIdElement::addTo, ADD_ORIGIN_ID_FILTER::accept);
  100.         ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
  101.     }

  102.     /**
  103.      * Stop appending origin-id elements to outgoing stanzas and remove the feature from disco.
  104.      */
  105.     public synchronized void disable() {
  106.         ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
  107.         connection().removeMessageInterceptor(OriginIdElement::addTo);
  108.     }

  109.     /**
  110.      * Return true, if we automatically append origin-id elements to outgoing stanzas.
  111.      *
  112.      * @return true if functionality is enabled, otherwise false.
  113.      */
  114.     public synchronized boolean isEnabled() {
  115.         ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection());
  116.         return disco.includesFeature(NAMESPACE);
  117.     }
  118. }