blob: dbe889d8edb084f90d39925fba0e08fc5473e22f [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smackx;
22
23import org.jivesoftware.smack.PacketCollector;
24import org.jivesoftware.smack.SmackConfiguration;
25import org.jivesoftware.smack.Connection;
26import org.jivesoftware.smack.XMPPException;
27import org.jivesoftware.smack.filter.*;
28import org.jivesoftware.smack.packet.IQ;
29import org.jivesoftware.smack.packet.Message;
30import org.jivesoftware.smack.packet.Packet;
31import org.jivesoftware.smackx.packet.DiscoverInfo;
32import org.jivesoftware.smackx.packet.DiscoverItems;
33import org.jivesoftware.smackx.packet.OfflineMessageInfo;
34import org.jivesoftware.smackx.packet.OfflineMessageRequest;
35
36import java.util.ArrayList;
37import java.util.Iterator;
38import java.util.List;
39
40/**
41 * The OfflineMessageManager helps manage offline messages even before the user has sent an
42 * available presence. When a user asks for his offline messages before sending an available
43 * presence then the server will not send a flood with all the offline messages when the user
44 * becomes online. The server will not send a flood with all the offline messages to the session
45 * that made the offline messages request or to any other session used by the user that becomes
46 * online.<p>
47 *
48 * Once the session that made the offline messages request has been closed and the user becomes
49 * offline in all the resources then the server will resume storing the messages offline and will
50 * send all the offline messages to the user when he becomes online. Therefore, the server will
51 * flood the user when he becomes online unless the user uses this class to manage his offline
52 * messages.
53 *
54 * @author Gaston Dombiak
55 */
56public class OfflineMessageManager {
57
58 private final static String namespace = "http://jabber.org/protocol/offline";
59
60 private Connection connection;
61
62 private PacketFilter packetFilter;
63
64 public OfflineMessageManager(Connection connection) {
65 this.connection = connection;
66 packetFilter =
67 new AndFilter(new PacketExtensionFilter("offline", namespace),
68 new PacketTypeFilter(Message.class));
69 }
70
71 /**
72 * Returns true if the server supports Flexible Offline Message Retrieval. When the server
73 * supports Flexible Offline Message Retrieval it is possible to get the header of the offline
74 * messages, get specific messages, delete specific messages, etc.
75 *
76 * @return a boolean indicating if the server supports Flexible Offline Message Retrieval.
77 * @throws XMPPException If the user is not allowed to make this request.
78 */
79 public boolean supportsFlexibleRetrieval() throws XMPPException {
80 DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(connection.getServiceName());
81 return info.containsFeature(namespace);
82 }
83
84 /**
85 * Returns the number of offline messages for the user of the connection.
86 *
87 * @return the number of offline messages for the user of the connection.
88 * @throws XMPPException If the user is not allowed to make this request or the server does
89 * not support offline message retrieval.
90 */
91 public int getMessageCount() throws XMPPException {
92 DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null,
93 namespace);
94 Form extendedInfo = Form.getFormFrom(info);
95 if (extendedInfo != null) {
96 String value = extendedInfo.getField("number_of_messages").getValues().next();
97 return Integer.parseInt(value);
98 }
99 return 0;
100 }
101
102 /**
103 * Returns an iterator on <tt>OfflineMessageHeader</tt> that keep information about the
104 * offline message. The OfflineMessageHeader includes a stamp that could be used to retrieve
105 * the complete message or delete the specific message.
106 *
107 * @return an iterator on <tt>OfflineMessageHeader</tt> that keep information about the offline
108 * message.
109 * @throws XMPPException If the user is not allowed to make this request or the server does
110 * not support offline message retrieval.
111 */
112 public Iterator<OfflineMessageHeader> getHeaders() throws XMPPException {
113 List<OfflineMessageHeader> answer = new ArrayList<OfflineMessageHeader>();
114 DiscoverItems items = ServiceDiscoveryManager.getInstanceFor(connection).discoverItems(
115 null, namespace);
116 for (Iterator<DiscoverItems.Item> it = items.getItems(); it.hasNext();) {
117 DiscoverItems.Item item = it.next();
118 answer.add(new OfflineMessageHeader(item));
119 }
120 return answer.iterator();
121 }
122
123 /**
124 * Returns an Iterator with the offline <tt>Messages</tt> whose stamp matches the specified
125 * request. The request will include the list of stamps that uniquely identifies
126 * the offline messages to retrieve. The returned offline messages will not be deleted
127 * from the server. Use {@link #deleteMessages(java.util.List)} to delete the messages.
128 *
129 * @param nodes the list of stamps that uniquely identifies offline message.
130 * @return an Iterator with the offline <tt>Messages</tt> that were received as part of
131 * this request.
132 * @throws XMPPException If the user is not allowed to make this request or the server does
133 * not support offline message retrieval.
134 */
135 public Iterator<Message> getMessages(final List<String> nodes) throws XMPPException {
136 List<Message> messages = new ArrayList<Message>();
137 OfflineMessageRequest request = new OfflineMessageRequest();
138 for (String node : nodes) {
139 OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
140 item.setAction("view");
141 request.addItem(item);
142 }
143 // Filter packets looking for an answer from the server.
144 PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
145 PacketCollector response = connection.createPacketCollector(responseFilter);
146 // Filter offline messages that were requested by this request
147 PacketFilter messageFilter = new AndFilter(packetFilter, new PacketFilter() {
148 public boolean accept(Packet packet) {
149 OfflineMessageInfo info = (OfflineMessageInfo) packet.getExtension("offline",
150 namespace);
151 return nodes.contains(info.getNode());
152 }
153 });
154 PacketCollector messageCollector = connection.createPacketCollector(messageFilter);
155 // Send the retrieval request to the server.
156 connection.sendPacket(request);
157 // Wait up to a certain number of seconds for a reply.
158 IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
159 // Stop queuing results
160 response.cancel();
161
162 if (answer == null) {
163 throw new XMPPException("No response from server.");
164 } else if (answer.getError() != null) {
165 throw new XMPPException(answer.getError());
166 }
167
168 // Collect the received offline messages
169 Message message = (Message) messageCollector.nextResult(
170 SmackConfiguration.getPacketReplyTimeout());
171 while (message != null) {
172 messages.add(message);
173 message =
174 (Message) messageCollector.nextResult(
175 SmackConfiguration.getPacketReplyTimeout());
176 }
177 // Stop queuing offline messages
178 messageCollector.cancel();
179 return messages.iterator();
180 }
181
182 /**
183 * Returns an Iterator with all the offline <tt>Messages</tt> of the user. The returned offline
184 * messages will not be deleted from the server. Use {@link #deleteMessages(java.util.List)}
185 * to delete the messages.
186 *
187 * @return an Iterator with all the offline <tt>Messages</tt> of the user.
188 * @throws XMPPException If the user is not allowed to make this request or the server does
189 * not support offline message retrieval.
190 */
191 public Iterator<Message> getMessages() throws XMPPException {
192 List<Message> messages = new ArrayList<Message>();
193 OfflineMessageRequest request = new OfflineMessageRequest();
194 request.setFetch(true);
195 // Filter packets looking for an answer from the server.
196 PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
197 PacketCollector response = connection.createPacketCollector(responseFilter);
198 // Filter offline messages that were requested by this request
199 PacketCollector messageCollector = connection.createPacketCollector(packetFilter);
200 // Send the retrieval request to the server.
201 connection.sendPacket(request);
202 // Wait up to a certain number of seconds for a reply.
203 IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
204 // Stop queuing results
205 response.cancel();
206
207 if (answer == null) {
208 throw new XMPPException("No response from server.");
209 } else if (answer.getError() != null) {
210 throw new XMPPException(answer.getError());
211 }
212
213 // Collect the received offline messages
214 Message message = (Message) messageCollector.nextResult(
215 SmackConfiguration.getPacketReplyTimeout());
216 while (message != null) {
217 messages.add(message);
218 message =
219 (Message) messageCollector.nextResult(
220 SmackConfiguration.getPacketReplyTimeout());
221 }
222 // Stop queuing offline messages
223 messageCollector.cancel();
224 return messages.iterator();
225 }
226
227 /**
228 * Deletes the specified list of offline messages. The request will include the list of
229 * stamps that uniquely identifies the offline messages to delete.
230 *
231 * @param nodes the list of stamps that uniquely identifies offline message.
232 * @throws XMPPException If the user is not allowed to make this request or the server does
233 * not support offline message retrieval.
234 */
235 public void deleteMessages(List<String> nodes) throws XMPPException {
236 OfflineMessageRequest request = new OfflineMessageRequest();
237 for (String node : nodes) {
238 OfflineMessageRequest.Item item = new OfflineMessageRequest.Item(node);
239 item.setAction("remove");
240 request.addItem(item);
241 }
242 // Filter packets looking for an answer from the server.
243 PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
244 PacketCollector response = connection.createPacketCollector(responseFilter);
245 // Send the deletion request to the server.
246 connection.sendPacket(request);
247 // Wait up to a certain number of seconds for a reply.
248 IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
249 // Stop queuing results
250 response.cancel();
251
252 if (answer == null) {
253 throw new XMPPException("No response from server.");
254 } else if (answer.getError() != null) {
255 throw new XMPPException(answer.getError());
256 }
257 }
258
259 /**
260 * Deletes all offline messages of the user.
261 *
262 * @throws XMPPException If the user is not allowed to make this request or the server does
263 * not support offline message retrieval.
264 */
265 public void deleteMessages() throws XMPPException {
266 OfflineMessageRequest request = new OfflineMessageRequest();
267 request.setPurge(true);
268 // Filter packets looking for an answer from the server.
269 PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
270 PacketCollector response = connection.createPacketCollector(responseFilter);
271 // Send the deletion request to the server.
272 connection.sendPacket(request);
273 // Wait up to a certain number of seconds for a reply.
274 IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
275 // Stop queuing results
276 response.cancel();
277
278 if (answer == null) {
279 throw new XMPPException("No response from server.");
280 } else if (answer.getError() != null) {
281 throw new XMPPException(answer.getError());
282 }
283 }
284}