001/**
002 *
003 * Copyright 2018 Paul Schaub
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.StanzaListener;
025import org.jivesoftware.smack.XMPPConnection;
026import org.jivesoftware.smack.XMPPConnectionRegistry;
027import org.jivesoftware.smack.filter.AndFilter;
028import org.jivesoftware.smack.filter.MessageTypeFilter;
029import org.jivesoftware.smack.filter.NotFilter;
030import org.jivesoftware.smack.filter.StanzaExtensionFilter;
031import org.jivesoftware.smack.filter.StanzaFilter;
032import org.jivesoftware.smack.filter.ToTypeFilter;
033import org.jivesoftware.smack.packet.Message;
034import org.jivesoftware.smack.packet.Stanza;
035import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
036import org.jivesoftware.smackx.sid.element.OriginIdElement;
037
038public final class StableUniqueStanzaIdManager extends Manager {
039
040    public static final String NAMESPACE = "urn:xmpp:sid:0";
041
042    private static final Map<XMPPConnection, StableUniqueStanzaIdManager> INSTANCES = new WeakHashMap<>();
043
044    // Filter for outgoing stanzas.
045    private static final StanzaFilter OUTGOING_FILTER = new AndFilter(
046            MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
047            ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
048
049    private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
050
051    // Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
052    private final StanzaListener stanzaListener = new StanzaListener() {
053        @Override
054        public void processStanza(Stanza stanza) {
055            OriginIdElement.addOriginId((Message) stanza);
056        }
057    };
058
059    static {
060        XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
061            @Override
062            public void connectionCreated(XMPPConnection connection) {
063                getInstanceFor(connection);
064            }
065        });
066    }
067
068    /**
069     * Private constructor.
070     * @param connection
071     */
072    private StableUniqueStanzaIdManager(XMPPConnection connection) {
073        super(connection);
074        enable();
075    }
076
077    /**
078     * Return an instance of the StableUniqueStanzaIdManager for the given connection.
079     *
080     * @param connection xmpp-connection
081     * @return manager instance for the connection
082     */
083    public static StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
084        StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
085        if (manager == null) {
086            manager = new StableUniqueStanzaIdManager(connection);
087            INSTANCES.put(connection, manager);
088        }
089        return manager;
090    }
091
092    /**
093     * Start appending origin-id elements to outgoing stanzas and add the feature to disco.
094     */
095    public synchronized void enable() {
096        ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
097        StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
098        connection().addStanzaInterceptor(stanzaListener, filter);
099    }
100
101    /**
102     * Stop appending origin-id elements to outgoing stanzas and remove the feature from disco.
103     */
104    public synchronized void disable() {
105        ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
106        connection().removeStanzaInterceptor(stanzaListener);
107    }
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}