Home | History | Annotate | Download | only in carbons
      1 /**
      2  * Copyright 2013 Georg Lukas
      3  *
      4  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.jivesoftware.smackx.carbons;
     18 
     19 import java.util.Collections;
     20 import java.util.Map;
     21 import java.util.WeakHashMap;
     22 
     23 import org.jivesoftware.smack.Connection;
     24 import org.jivesoftware.smack.ConnectionCreationListener;
     25 import org.jivesoftware.smack.PacketCollector;
     26 import org.jivesoftware.smack.PacketListener;
     27 import org.jivesoftware.smack.SmackConfiguration;
     28 import org.jivesoftware.smack.XMPPException;
     29 import org.jivesoftware.smack.filter.PacketIDFilter;
     30 import org.jivesoftware.smack.packet.IQ;
     31 import org.jivesoftware.smack.packet.Message;
     32 import org.jivesoftware.smack.packet.Packet;
     33 import org.jivesoftware.smackx.ServiceDiscoveryManager;
     34 import org.jivesoftware.smackx.packet.DiscoverInfo;
     35 
     36 /**
     37  * Packet extension for XEP-0280: Message Carbons. This class implements
     38  * the manager for registering {@link Carbon} support, enabling and disabling
     39  * message carbons.
     40  *
     41  * You should call enableCarbons() before sending your first undirected
     42  * presence.
     43  *
     44  * @author Georg Lukas
     45  */
     46 public class CarbonManager {
     47 
     48     private static Map<Connection, CarbonManager> instances =
     49             Collections.synchronizedMap(new WeakHashMap<Connection, CarbonManager>());
     50 
     51     static {
     52         Connection.addConnectionCreationListener(new ConnectionCreationListener() {
     53             public void connectionCreated(Connection connection) {
     54                 new CarbonManager(connection);
     55             }
     56         });
     57     }
     58 
     59     private Connection connection;
     60     private volatile boolean enabled_state = false;
     61 
     62     private CarbonManager(Connection connection) {
     63         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
     64         sdm.addFeature(Carbon.NAMESPACE);
     65         this.connection = connection;
     66         instances.put(connection, this);
     67     }
     68 
     69     /**
     70      * Obtain the CarbonManager responsible for a connection.
     71      *
     72      * @param connection the connection object.
     73      *
     74      * @return a CarbonManager instance
     75      */
     76     public static CarbonManager getInstanceFor(Connection connection) {
     77         CarbonManager carbonManager = instances.get(connection);
     78 
     79         if (carbonManager == null) {
     80             carbonManager = new CarbonManager(connection);
     81         }
     82 
     83         return carbonManager;
     84     }
     85 
     86     private IQ carbonsEnabledIQ(final boolean new_state) {
     87         IQ setIQ = new IQ() {
     88             public String getChildElementXML() {
     89                 return "<" + (new_state? "enable" : "disable") + " xmlns='" + Carbon.NAMESPACE + "'/>";
     90             }
     91         };
     92         setIQ.setType(IQ.Type.SET);
     93         return setIQ;
     94     }
     95 
     96     /**
     97      * Returns true if XMPP Carbons are supported by the server.
     98      *
     99      * @return true if supported
    100      */
    101     public boolean isSupportedByServer() {
    102         try {
    103             DiscoverInfo result = ServiceDiscoveryManager
    104                 .getInstanceFor(connection).discoverInfo(connection.getServiceName());
    105             return result.containsFeature(Carbon.NAMESPACE);
    106         }
    107         catch (XMPPException e) {
    108             return false;
    109         }
    110     }
    111 
    112     /**
    113      * Notify server to change the carbons state. This method returns
    114      * immediately and changes the variable when the reply arrives.
    115      *
    116      * You should first check for support using isSupportedByServer().
    117      *
    118      * @param new_state whether carbons should be enabled or disabled
    119      */
    120     public void sendCarbonsEnabled(final boolean new_state) {
    121         IQ setIQ = carbonsEnabledIQ(new_state);
    122 
    123         connection.addPacketListener(new PacketListener() {
    124             public void processPacket(Packet packet) {
    125                 IQ result = (IQ)packet;
    126                 if (result.getType() == IQ.Type.RESULT) {
    127                     enabled_state = new_state;
    128                 }
    129                 connection.removePacketListener(this);
    130             }
    131         }, new PacketIDFilter(setIQ.getPacketID()));
    132 
    133         connection.sendPacket(setIQ);
    134     }
    135 
    136     /**
    137      * Notify server to change the carbons state. This method blocks
    138      * some time until the server replies to the IQ and returns true on
    139      * success.
    140      *
    141      * You should first check for support using isSupportedByServer().
    142      *
    143      * @param new_state whether carbons should be enabled or disabled
    144      *
    145      * @return true if the operation was successful
    146      */
    147     public boolean setCarbonsEnabled(final boolean new_state) {
    148         if (enabled_state == new_state)
    149             return true;
    150 
    151         IQ setIQ = carbonsEnabledIQ(new_state);
    152 
    153         PacketCollector collector =
    154                 connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID()));
    155         connection.sendPacket(setIQ);
    156         IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    157         collector.cancel();
    158 
    159         if (result != null && result.getType() == IQ.Type.RESULT) {
    160             enabled_state = new_state;
    161             return true;
    162         }
    163         return false;
    164     }
    165 
    166     /**
    167      * Helper method to enable carbons.
    168      *
    169      * @return true if the operation was successful
    170      */
    171     public boolean enableCarbons() {
    172         return setCarbonsEnabled(true);
    173     }
    174 
    175     /**
    176      * Helper method to disable carbons.
    177      *
    178      * @return true if the operation was successful
    179      */
    180     public boolean disableCarbons() {
    181         return setCarbonsEnabled(false);
    182     }
    183 
    184     /**
    185      * Check if carbons are enabled on this connection.
    186      */
    187     public boolean getCarbonsEnabled() {
    188         return this.enabled_state;
    189     }
    190 
    191     /**
    192      * Obtain a Carbon from a message, if available.
    193      *
    194      * @param msg Message object to check for carbons
    195      *
    196      * @return a Carbon if available, null otherwise.
    197      */
    198     public static Carbon getCarbon(Message msg) {
    199         Carbon cc = (Carbon)msg.getExtension("received", Carbon.NAMESPACE);
    200         if (cc == null)
    201             cc = (Carbon)msg.getExtension("sent", Carbon.NAMESPACE);
    202         return cc;
    203     }
    204 
    205     /**
    206      * Mark a message as "private", so it will not be carbon-copied.
    207      *
    208      * @param msg Message object to mark private
    209      */
    210     public static void disableCarbons(Message msg) {
    211         msg.addExtension(new Carbon.Private());
    212     }
    213 }
    214