1 /** 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 21 package org.jivesoftware.smackx; 22 23 import org.jivesoftware.smack.PacketCollector; 24 import org.jivesoftware.smack.SmackConfiguration; 25 import org.jivesoftware.smack.Connection; 26 import org.jivesoftware.smack.XMPPException; 27 import org.jivesoftware.smack.filter.*; 28 import org.jivesoftware.smack.packet.IQ; 29 import org.jivesoftware.smack.packet.Message; 30 import org.jivesoftware.smack.packet.Packet; 31 import org.jivesoftware.smackx.packet.DiscoverInfo; 32 import org.jivesoftware.smackx.packet.DiscoverItems; 33 import org.jivesoftware.smackx.packet.OfflineMessageInfo; 34 import org.jivesoftware.smackx.packet.OfflineMessageRequest; 35 36 import java.util.ArrayList; 37 import java.util.Iterator; 38 import 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 */ 56 public 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 } 285