Skip to content

Commit

Permalink
Rebase to current master: add XEP-0333 (Chatmarker archiving)
Browse files Browse the repository at this point in the history
  • Loading branch information
mightymop committed Dec 23, 2021
1 parent 40d93cb commit a760046
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/i18n/monitoring_i18n.properties
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ archive.settings.logs.link.unsecure=End-users can access the logs (using an unen
archive.settings.logs.link.secure=Log files are also available via an encrypted HTTPS address at: <a href="{0}">{0}</a>.
archive.settings.logs.public.enable=Public Group Chat Weblog
archive.settings.logs.public.enable.description=Expose log files of public group chats through a web interface.
archive.settings.chatmarker=Archive chatmarkers (XEP-0333)

archive.search.title = Search Archive
archive.search.participants = Participant(s):
Expand Down
20 changes: 18 additions & 2 deletions src/java/org/jivesoftware/openfire/archive/ArchiveInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void interceptPacket(Packet packet, Session session, boolean incoming, bo
// Ignore any messages that don't have a body so that we skip events.
// Note: XHTML messages should always include a body so we should be ok. It's
// possible that we may need special XHTML filtering in the future, however.
if (message.getBody() != null) {
if (message.getBody() != null||(message.getBody()==null&&this.conversationManager.isChatmarkerArchivingEnabled())) {
// Only process messages that are between two users, group chat rooms, or gateways.
if (conversationManager.isConversation(message)) {
// Process this event in the senior cluster member or local JVM when not in a cluster
Expand All @@ -74,11 +74,27 @@ public void interceptPacket(Packet packet, Session session, boolean incoming, bo
JID sender = message.getFrom();
JID receiver = message.getTo();
ConversationEventsQueue eventsQueue = conversationManager.getConversationEventsQueue();
eventsQueue.addChatEvent(conversationManager.getConversationKey(sender, receiver),
if (message.getBody()!=null)
{
eventsQueue.addChatEvent(conversationManager.getConversationKey(sender, receiver),
ConversationEvent.chatMessageReceived(sender, receiver,
conversationManager.isMessageArchivingEnabled() ? message.getBody() : null,
conversationManager.isMessageArchivingEnabled() ? message.toXML() : null,
new Date()));
}
else
{
String stanza = message.toXML();
ChatMarker.TYPE markertype = ChatMarker.searchForXep0333(stanza);

if (markertype!=ChatMarker.TYPE.NONE)
{
eventsQueue.addChatEvent(conversationManager.getConversationKey(sender, receiver),
ConversationEvent.chatmarkerMessageReceived(sender, receiver,markertype,
conversationManager.isMessageArchivingEnabled() ? message.toXML() : null,
new Date()));
}
}
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions src/java/org/jivesoftware/openfire/archive/ChatMarker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.jivesoftware.openfire.archive;

public class ChatMarker {

public enum TYPE
{
NONE,
MARKABLE,
RECEIVED,
DISPLAYED,
ACKNOWLEGED;
}

public static TYPE searchForXep0333(String stanza)
{
if (stanza==null)
{
return TYPE.NONE;
}

int idxmarkable = stanza.indexOf("<markable");
int idxreceived= stanza.indexOf("<received");
int idxdisplayed = stanza.indexOf("<displayed");
int idxacknowled = stanza.indexOf("<acknowledged");

if (idxmarkable!=-1)
{
int idxEnd = stanza.indexOf("/>",idxmarkable);
if (idxEnd==-1)
{
idxEnd = stanza.indexOf("</markable>",idxmarkable);
}
return idxEnd!=-1?(stanza.substring(idxmarkable, idxEnd).contains("urn:xmpp:chat-markers:0")?TYPE.MARKABLE:TYPE.NONE):TYPE.NONE;
}
else
if (idxreceived!=-1)
{
int idxEnd = stanza.indexOf("/>",idxreceived);
if (idxEnd==-1)
{
idxEnd = stanza.indexOf("</received>",idxmarkable);
}
return idxEnd!=-1?(stanza.substring(idxreceived, idxEnd).contains("urn:xmpp:chat-markers:0")?TYPE.RECEIVED:TYPE.NONE):TYPE.NONE;
}
else
if (idxdisplayed!=-1)
{
int idxEnd = stanza.indexOf("/>",idxdisplayed);
if (idxEnd==-1)
{
idxEnd = stanza.indexOf("</displayed>",idxmarkable);
}
return idxEnd!=-1?(stanza.substring(idxdisplayed, idxEnd).contains("urn:xmpp:chat-markers:0")?TYPE.DISPLAYED:TYPE.NONE):TYPE.NONE;
}
else
if (idxacknowled!=-1)
{
int idxEnd = stanza.indexOf("/>",idxacknowled);
if (idxEnd==-1)
{
idxEnd = stanza.indexOf("</acknowledged>",idxmarkable);
}
return idxEnd!=-1?(stanza.substring(idxacknowled, idxEnd).contains("urn:xmpp:chat-markers:0")?TYPE.ACKNOWLEGED:TYPE.NONE):TYPE.NONE;
}
else
return TYPE.NONE;
}
}
12 changes: 12 additions & 0 deletions src/java/org/jivesoftware/openfire/archive/ConversationEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public class ConversationEvent {

private String nickname;

private ChatMarker.TYPE marker;

/**
* Do not use this constructor. It only exists for serialization purposes.
*/
Expand Down Expand Up @@ -195,4 +197,14 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(type, date, body, stanza, sender, receiver, roomJID, user, nickname);
}

public static ConversationEvent chatmarkerMessageReceived(JID sender, JID receiver, ChatMarker.TYPE marker, String stanza, Date date) {
ConversationEvent event = new ConversationEvent();
event.type = Type.chatMessageReceived;
event.sender = sender;
event.receiver = receiver;
event.marker = marker;
event.date = date;
return event;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public class ConversationManager implements ComponentEventListener{
* Flag that indicates if messages of one-to-one chats should be archived.
*/
private boolean messageArchivingEnabled;
private boolean chatmarkerArchivingEnabled;
/**
* Flag that indicates if messages of group chats (in MUC rooms) should be archived.
*/
Expand Down Expand Up @@ -137,6 +138,13 @@ public class ConversationManager implements ComponentEventListener{
.setPlugin(MonitoringConstants.PLUGIN_NAME)
.build();

public static SystemProperty<Boolean> MESSAGEMARKER_ARCHIVING_ENABLED = SystemProperty.Builder.ofType(Boolean.class)
.setKey("conversation.chatmarkerArchiving")
.setDefaultValue(false)
.setDynamic(true)
.setPlugin(MonitoringConstants.PLUGIN_NAME)
.build();

public static SystemProperty<Boolean> MESSAGE_ARCHIVING_ENABLED = SystemProperty.Builder.ofType(Boolean.class)
.setKey("conversation.messageArchiving")
.setDefaultValue(false)
Expand Down Expand Up @@ -207,6 +215,7 @@ public ConversationManager(TaskEngine taskEngine) {
public void start() {
metadataArchivingEnabled = METADATA_ARCHIVING_ENABLED.getValue();
messageArchivingEnabled = MESSAGE_ARCHIVING_ENABLED.getValue();
chatmarkerArchivingEnabled = MESSAGEMARKER_ARCHIVING_ENABLED.getValue();
if (messageArchivingEnabled && !metadataArchivingEnabled) {
Log.warn("Metadata archiving must be enabled when message archiving is enabled. Overriding setting.");
metadataArchivingEnabled = true;
Expand Down Expand Up @@ -391,6 +400,15 @@ public boolean isArchivingEnabled() {
return isMessageArchivingEnabled() || isRoomArchivingEnabled();
}

/**
* Returns true if chatmarker archiving is enabled, otherwise false
*
* @return
*/
public boolean isChatmarkerArchivingEnabled() {
return chatmarkerArchivingEnabled;
}

/**
* Returns true if message archiving is enabled for one-to-one chats. When enabled, all messages in one-to-one conversations are stored in the
* database. Note: it's not possible for meta-data archiving to be disabled when message archiving is enabled; enabling message archiving
Expand Down Expand Up @@ -418,6 +436,17 @@ public void setMessageArchivingEnabled(boolean enabled) {
}
}

/**
* Sets whether message chatmarker archiving is enabled
*
* @param enabled
* true if chatmarker should be archived.
*/
public void setChatmarkerArchivingEnabled(boolean enabled) {
this.messageArchivingEnabled = enabled;
MESSAGEMARKER_ARCHIVING_ENABLED.setValue(enabled);
}

/**
* Returns true if message archiving is enabled for group chats. When enabled, all messages in group conversations are stored in the database
* unless a list of rooms was specified in {@link #getRoomsArchived()} . Note: it's not possible for meta-data archiving to be disabled when room
Expand Down Expand Up @@ -737,6 +766,7 @@ void processMessage(JID sender, JID receiver, String body, String stanza, Date d
synchronized (conversationKey.intern()) {
Conversation conversation = conversations.get(conversationKey);
// Create a new conversation if necessary.
ChatMarker.TYPE chatmarker=ChatMarker.searchForXep0333(stanza);
if (conversation == null) {
Collection<JID> participants = new ArrayList<>(2);
participants.add(sender);
Expand Down Expand Up @@ -784,7 +814,7 @@ else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime.t
conversationArchiver.archive(conversation);
}
if (messageArchivingEnabled) {
if (body != null) {
if (body != null||(body==null&&chatmarkerArchivingEnabled&&chatmarker!=ChatMarker.TYPE.NONE)) {
/* OF-677 - Workaround to prevent null messages being archived */
messageArchiver.archive(new ArchivedMessage(conversation.getConversationID(), sender, receiver, date, body, stanza, false, null) );
}
Expand Down Expand Up @@ -819,6 +849,7 @@ void processRoomMessage(JID roomJID, JID sender, JID receiverIfPM, String nickna
synchronized (conversationKey.intern()) {
Conversation conversation = conversations.get(conversationKey);
// Create a new conversation if necessary.
ChatMarker.TYPE chatmarker=ChatMarker.searchForXep0333(stanza);
if (conversation == null) {
// Make sure that the user joined the conversation before a message was received
Date start = new Date(date.getTime() - 1);
Expand Down Expand Up @@ -850,7 +881,7 @@ else if ((date.getTime() - conversation.getLastActivity().getTime() > idleTime.t
}
if (roomArchivingEnabled && (roomsArchived.isEmpty() || roomsArchived.contains(roomJID.getNode()))) {
JID jid = new JID(roomJID + "/" + nickname);
if (body != null) {
if (body != null||(body==null&&chatmarkerArchivingEnabled&&chatmarker!=ChatMarker.TYPE.NONE)) {
/* OF-677 - Workaround to prevent null messages being archived */
messageArchiver.archive( new ArchivedMessage(conversation.getConversationID(), sender, jid, date, body, roomArchivingStanzasEnabled ? stanza : "", false, receiverIfPM));
}
Expand Down Expand Up @@ -1503,6 +1534,10 @@ public void propertySet(String property, Map<String, Object> params) {
maxTime = DEFAULT_MAX_TIME;
}
}
else if (property.equals("conversation.chatmarkerArchiving")) {
String value = (String) params.get("value");
chatmarkerArchivingEnabled = Boolean.valueOf(value);
}
}

public void propertyDeleted(String property, Map<String, Object> params) {
Expand All @@ -1524,9 +1559,11 @@ public void propertyDeleted(String property, Map<String, Object> params) {
setMaxAge(MAX_AGE.getDefaultValue());
} else if (property.equals("conversation.maxRetrievable")) {
setMaxRetrievable(MAX_RETRIEVABLE.getDefaultValue());
} else if (property.equals("conversation.maxTimeDebug")) {
} else if (property.equals("conversation.maxTimeDebug")) {
Log.info("Monitoring plugin max time reset back to " + DEFAULT_MAX_TIME + " minutes");
setMaxTime(MAX_TIME.getDefaultValue());
} else if (property.equals("conversation.chatmarkerArchiving")) {
setChatmarkerArchivingEnabled(MESSAGEMARKER_ARCHIVING_ENABLED.getDefaultValue());
}
}

Expand Down
61 changes: 56 additions & 5 deletions src/java/org/jivesoftware/openfire/archive/ConversationUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,13 @@ private ByteArrayOutputStream buildPDFContent(ConversationManager conversationMa
String prefix;

if (!message.isRoomEvent()) {
/*
* If body is null, then it is a chatmarker, so we add the ressource to see which device has sent the marker.
* */
if (to == null) {
prefix = "[" + time + "] " + from + ": ";
prefix = "[" + time + "] " + from+(body==null?(" ("+message.getToJID().getResource()+")"):"")+ ": ";
} else {
prefix = "[" + time + "] " + from + " -> " + to + ": ";
prefix = "[" + time + "] " + from+(body==null?(" ("+message.getToJID().getResource()+")"):"")+ " -> " + to + ": ";
}
Color color = colorMap.get(message.getFromJID());
if (color == null) {
Expand All @@ -230,12 +233,35 @@ private ByteArrayOutputStream buildPDFContent(ConversationManager conversationMa
}

messageParagraph.add(new Text(prefix).setFont(PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD)).setFontColor(color));
messageParagraph.add(new Text(body).setFontColor(ColorConstants.BLACK));
messageParagraph.add(new Text(body==null?"":body).setFontColor(ColorConstants.BLACK));
}
else {
prefix = "[" + time + "] ";
messageParagraph.add( new Text(prefix)).setFont(PdfFontFactory.createFont(StandardFonts.HELVETICA_OBLIQUE)).setFontColor(ColorConstants.MAGENTA);
messageParagraph.add( new Text(body).setFont(PdfFontFactory.createFont(StandardFonts.HELVETICA_OBLIQUE)).setFontColor(ColorConstants.MAGENTA));
messageParagraph.add( new Text(body==null?"":body).setFont(PdfFontFactory.createFont(StandardFonts.HELVETICA_OBLIQUE)).setFontColor(ColorConstants.MAGENTA));
}

if (body==null)
{
ChatMarker.TYPE chatmarker = ChatMarker.searchForXep0333(message.getStanza());
switch (chatmarker)
{
case NONE:
messageParagraph.add("--unknown message--");
break;
case MARKABLE:
messageParagraph.add("--message markable--");
break;
case RECEIVED:
messageParagraph.add("--message received--");
break;
case DISPLAYED:
messageParagraph.add("--message displayed--");
break;
case ACKNOWLEGED:
messageParagraph.add("--message acknowleged--");
break;
}
}
messageParagraph.add(new Text("\n"));
}
Expand Down Expand Up @@ -307,10 +333,35 @@ private ConversationInfo toConversationInfo(ConversationManager conversationMana
if (conversation.getRoom() != null) {
from = message.getToJID().getResource();
}
from = StringUtils.escapeHTMLTags(from);
/*
* If body is null, then it is a chatmarker, so we add the ressource to see which device has sent the marker.
* */
from = StringUtils.escapeHTMLTags(from)+(message.getBody()==null?(" ("+message.getFromJID().getResource()+")"):"");
to = to == null ? null : StringUtils.escapeHTMLTags(to);
String cssLabel = cssLabels.get(message.getFromJID().toBareJID());
String body = StringUtils.escapeHTMLTags(message.getBody());
if (body==null)
{
ChatMarker.TYPE chatmarker = ChatMarker.searchForXep0333(message.getStanza());
switch (chatmarker)
{
case NONE:
body="--unknown message--";
break;
case MARKABLE:
body="--message markable--";
break;
case RECEIVED:
body="--message received--";
break;
case DISPLAYED:
body="--message displayed--";
break;
case ACKNOWLEGED:
body="--message acknowleged--";
break;
}
}
builder.append("<tr valign=top>");
if (!message.isRoomEvent()) {
builder.append("<td width=1% nowrap class=" + cssLabel + ">").append("[").append(time).append("]").append("</td>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,25 @@ public void messageReceived(JID roomJID, JID user, String nickname, Message mess
conversationManager.getRoomsArchived().isEmpty() ||
conversationManager.getRoomsArchived().contains(roomJID.getNode()));

ConversationEventsQueue eventsQueue = conversationManager.getConversationEventsQueue();
eventsQueue.addGroupChatEvent(conversationManager.getRoomConversationKey(roomJID),
if (withBody)
{
ConversationEventsQueue eventsQueue = conversationManager.getConversationEventsQueue();
eventsQueue.addGroupChatEvent(conversationManager.getRoomConversationKey(roomJID),
ConversationEvent.roomMessageReceived(roomJID, user, null, nickname, withBody ? message.getBody() : null, message.toXML(), now));
}
else
{
String stanza = message.toXML();
ChatMarker.TYPE markertype = ChatMarker.searchForXep0333(stanza);

if (markertype!=ChatMarker.TYPE.NONE)
{
ConversationEventsQueue eventsQueue = conversationManager.getConversationEventsQueue();
eventsQueue.addGroupChatEvent(conversationManager.getRoomConversationKey(roomJID),
ConversationEvent.chatmarkerMessageReceived(roomJID, user, markertype,stanza,
new Date()));
}
}
}
}

Expand Down
Loading

0 comments on commit a760046

Please sign in to comment.