blob: f44701afd8690703cb84afbe16bd62a8af7e1d29 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
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
17package org.jivesoftware.smackx.carbons;
18
19import java.util.Collections;
20import java.util.Map;
21import java.util.WeakHashMap;
22
23import org.jivesoftware.smack.Connection;
24import org.jivesoftware.smack.ConnectionCreationListener;
25import org.jivesoftware.smack.PacketCollector;
26import org.jivesoftware.smack.PacketListener;
27import org.jivesoftware.smack.SmackConfiguration;
28import org.jivesoftware.smack.XMPPException;
29import org.jivesoftware.smack.filter.PacketIDFilter;
30import org.jivesoftware.smack.packet.IQ;
31import org.jivesoftware.smack.packet.Message;
32import org.jivesoftware.smack.packet.Packet;
33import org.jivesoftware.smackx.ServiceDiscoveryManager;
34import 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 */
46public 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}