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