• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
Connection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(Connection connection) { new CarbonManager(connection); } })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 
CarbonManager(Connection connection)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      */
getInstanceFor(Connection connection)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 
carbonsEnabledIQ(final boolean new_state)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      */
isSupportedByServer()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      */
sendCarbonsEnabled(final boolean new_state)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      */
setCarbonsEnabled(final boolean new_state)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      */
enableCarbons()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      */
disableCarbons()180     public boolean disableCarbons() {
181         return setCarbonsEnabled(false);
182     }
183 
184     /**
185      * Check if carbons are enabled on this connection.
186      */
getCarbonsEnabled()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      */
getCarbon(Message msg)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      */
disableCarbons(Message msg)210     public static void disableCarbons(Message msg) {
211         msg.addExtension(new Carbon.Private());
212     }
213 }
214