001/** 002 * 003 * Copyright 2018 Paul Schaub, 2020 Florian Schmaus 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.sid; 018 019import java.util.Map; 020import java.util.WeakHashMap; 021 022import org.jivesoftware.smack.ConnectionCreationListener; 023import org.jivesoftware.smack.Manager; 024import org.jivesoftware.smack.XMPPConnection; 025import org.jivesoftware.smack.XMPPConnectionRegistry; 026import org.jivesoftware.smack.filter.AndFilter; 027import org.jivesoftware.smack.filter.MessageTypeFilter; 028import org.jivesoftware.smack.filter.NotFilter; 029import org.jivesoftware.smack.filter.StanzaExtensionFilter; 030import org.jivesoftware.smack.filter.StanzaFilter; 031import org.jivesoftware.smack.filter.ToTypeFilter; 032 033import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 034import org.jivesoftware.smackx.sid.element.OriginIdElement; 035 036/** 037 * Manager class for Stable and Unique Stanza IDs. 038 * 039 * In order to start automatically appending origin ids to outgoing messages, use {@link #enable()}. 040 * This will announce support via the {@link ServiceDiscoveryManager}. If you want to stop appending origin-ids 041 * and de-announce support, call {@link #disable()}. 042 * 043 * @see <a href="https://xmpp.org/extensions/xep-0359.html">XEP-0359: Stable and Unique Stanza IDs</a> 044 */ 045public final class StableUniqueStanzaIdManager extends Manager { 046 047 public static final String NAMESPACE = "urn:xmpp:sid:0"; 048 049 private static final Map<XMPPConnection, StableUniqueStanzaIdManager> INSTANCES = new WeakHashMap<>(); 050 051 private static boolean enabledByDefault = true; 052 053 // Filter for outgoing stanzas. 054 private static final StanzaFilter OUTGOING_FILTER = new AndFilter( 055 MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE, 056 ToTypeFilter.ENTITY_FULL_OR_BARE_JID); 057 058 // Filter that filters for messages with an origin id 059 private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE); 060 061 // We need a filter for outgoing messages that do not carry an origin-id already. 062 private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER)); 063 064 static { 065 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 066 @Override 067 public void connectionCreated(XMPPConnection connection) { 068 if (enabledByDefault) { 069 getInstanceFor(connection).enable(); 070 } 071 } 072 }); 073 } 074 075 /** 076 * Private constructor. 077 * @param connection XMPP connection 078 */ 079 private StableUniqueStanzaIdManager(XMPPConnection connection) { 080 super(connection); 081 } 082 083 public static void setEnabledByDefault(boolean enabled) { 084 enabledByDefault = enabled; 085 } 086 087 /** 088 * Return an instance of the StableUniqueStanzaIdManager for the given connection. 089 * 090 * @param connection xmpp-connection 091 * @return manager instance for the connection 092 */ 093 public static synchronized StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) { 094 StableUniqueStanzaIdManager manager = INSTANCES.get(connection); 095 if (manager == null) { 096 manager = new StableUniqueStanzaIdManager(connection); 097 INSTANCES.put(connection, manager); 098 } 099 return manager; 100 } 101 102 /** 103 * Start appending origin-id elements to outgoing stanzas and add the feature to disco. 104 */ 105 public synchronized void enable() { 106 connection().addMessageInterceptor(OriginIdElement::addTo, ADD_ORIGIN_ID_FILTER::accept); 107 ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); 108 } 109 110 /** 111 * Stop appending origin-id elements to outgoing stanzas and remove the feature from disco. 112 */ 113 public synchronized void disable() { 114 ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); 115 connection().removeMessageInterceptor(OriginIdElement::addTo); 116 } 117 118 /** 119 * Return true, if we automatically append origin-id elements to outgoing stanzas. 120 * 121 * @return true if functionality is enabled, otherwise false. 122 */ 123 public synchronized boolean isEnabled() { 124 ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection()); 125 return disco.includesFeature(NAMESPACE); 126 } 127}