Commit e3fdaae5 authored by csh's avatar csh

OF-722 Made OfflineMessageStore XEP-160 conform + Tests

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13945 b35dd754-fafc-0310-a699-88a17e54d16e
parent 4697f0b4
...@@ -20,22 +20,9 @@ ...@@ -20,22 +20,9 @@
package org.jivesoftware.openfire; package org.jivesoftware.openfire;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.QName;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.database.SequenceManager; import org.jivesoftware.database.SequenceManager;
...@@ -55,6 +42,16 @@ import org.slf4j.LoggerFactory; ...@@ -55,6 +42,16 @@ import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Represents the user's offline message storage. A message store holds messages that were * Represents the user's offline message storage. A message store holds messages that were
* sent to the user while they were unavailable. The user can retrieve their messages by * sent to the user while they were unavailable. The user can retrieve their messages by
...@@ -126,14 +123,9 @@ public class OfflineMessageStore extends BasicModule implements UserEventListene ...@@ -126,14 +123,9 @@ public class OfflineMessageStore extends BasicModule implements UserEventListene
if (message == null) { if (message == null) {
return; return;
} }
// ignore empty bodied message (typically chat-state notifications). if(!shouldStoreMessage(message)) {
if (message.getBody() == null || message.getBody().length() == 0) {
// allow empty pubsub messages (OF-191)
if (message.getChildElement("event", "http://jabber.org/protocol/pubsub#event") == null)
{
return; return;
} }
}
JID recipient = message.getTo(); JID recipient = message.getTo();
String username = recipient.getNode(); String username = recipient.getNode();
// If the username is null (such as when an anonymous user), don't store. // If the username is null (such as when an anonymous user), don't store.
...@@ -470,4 +462,61 @@ public class OfflineMessageStore extends BasicModule implements UserEventListene ...@@ -470,4 +462,61 @@ public class OfflineMessageStore extends BasicModule implements UserEventListene
// Remove this module as a user event listener // Remove this module as a user event listener
UserEventDispatcher.removeListener(this); UserEventDispatcher.removeListener(this);
} }
/**
* Decide whether a message should be stored offline according to XEP-0160 and XEP-0334.
*
* @param message
* @return <code>true</code> if the message should be stored offline, <code>false</code> otherwise.
*/
static boolean shouldStoreMessage(final Message message) {
// XEP-0334: Implement the <no-store/> hint to override offline storage
if (message.getChildElement("no-store", "urn:xmpp:hints") != null) {
return false;
}
switch (message.getType()) {
case chat:
// XEP-0160: Messages with a 'type' attribute whose value is "chat" SHOULD be stored offline, with the exception of messages that contain only Chat State Notifications (XEP-0085) [7] content
// Iterate through the child elements to see if we can find anything that's not a chat state notification or
// real time text notification
Iterator<?> it = message.getElement().elementIterator();
while (it.hasNext()) {
Object item = it.next();
if (item instanceof Element) {
Element el = (Element) item;
if (!el.getNamespaceURI().equals("http://jabber.org/protocol/chatstates")
&& !(el.getQName().equals(QName.get("rtt", "urn:xmpp:rtt:0")))
) {
return true;
}
}
}
return false;
case groupchat:
case headline:
// XEP-0160: "groupchat" message types SHOULD NOT be stored offline
// XEP-0160: "headline" message types SHOULD NOT be stored offline
return false;
case error:
// XEP-0160: "error" message types SHOULD NOT be stored offline,
// although a server MAY store advanced message processing errors offline
if (message.getChildElement("amp", "http://jabber.org/protocol/amp") == null) {
return false;
}
break;
default:
// XEP-0160: Messages with a 'type' attribute whose value is "normal" (or messages with no 'type' attribute) SHOULD be stored offline.
break;
}
return true;
}
} }
\ No newline at end of file
package org.jivesoftware.openfire;
import junit.framework.Assert;
import org.junit.Test;
import org.xmpp.packet.Message;
import org.xmpp.packet.PacketExtension;
/**
* This tests the business rules for storing messages as described in <a href="http://xmpp.org/extensions/xep-0160.html#types">3. Handling of Message Types</a>.
*
* @author csh
*/
public class OfflineMessageStoreTest {
@Test
public void shouldNotStoreGroupChatMessages() {
// XEP-0160: "groupchat" message types SHOULD NOT be stored offline
Message message = new Message();
message.setType(Message.Type.groupchat);
Assert.assertFalse(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldNotStoreHeadlineMessages() {
// XEP-0160: "headline" message types SHOULD NOT be stored offline
Message message = new Message();
message.setType(Message.Type.headline);
Assert.assertFalse(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldNotStoreErrorMessages() {
// XEP-0160: "error" message types SHOULD NOT be stored offline,
Message message = new Message();
message.setType(Message.Type.error);
Assert.assertFalse(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldStoreNormalMessages() {
// XEP-0160: Messages with a 'type' attribute whose value is "normal" (or messages with no 'type' attribute) SHOULD be stored offline.
Message message = new Message();
message.setType(Message.Type.normal);
Assert.assertTrue(OfflineMessageStore.shouldStoreMessage(message));
Message message2 = new Message();
Assert.assertTrue(OfflineMessageStore.shouldStoreMessage(message2));
}
@Test
public void shouldNotStoreEmptyChatMessages() {
// XEP-0160: "chat" message types SHOULD be stored offline unless they only contain chat state notifications
Message message = new Message();
message.setType(Message.Type.chat);
Assert.assertFalse(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldStoreNonEmptyChatMessages() {
// XEP-0160: "chat" message types SHOULD be stored offline unless they only contain chat state notifications
Message message = new Message();
message.setType(Message.Type.chat);
message.setBody(" ");
Assert.assertTrue(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldNotStoreEmptyChatMessagesWithOnlyChatStates() {
Message message = new Message();
message.setType(Message.Type.chat);
PacketExtension chatState = new PacketExtension("composing", "http://jabber.org/protocol/chatstates");
message.addExtension(chatState);
Assert.assertFalse(OfflineMessageStore.shouldStoreMessage(message));
}
@Test
public void shouldStoreEmptyChatMessagesWithOtherExtensions() {
Message message = new Message();
message.setType(Message.Type.chat);
PacketExtension chatState = new PacketExtension("composing", "http://jabber.org/protocol/chatstates");
message.addExtension(chatState);
PacketExtension packetExtension2 = new PacketExtension("received", "urn:xmpp:receipts");
message.addExtension(packetExtension2);
Assert.assertTrue(OfflineMessageStore.shouldStoreMessage(message));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment