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