From d5ec91815eae1fb2a015648207d16b873b4c8f3b Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Mon, 7 Sep 2026 21:38:39 +0200 Subject: [PATCH 1/3] OF-3364: Fix getMaxMessages() reporting unlimited for default-type rooms MUCRoomHistory.getMaxMessages() switched on the room's own HistoryStrategy type, which is always 'defaulType' (sic) for rooms that inherit their history settings. This caused the method to always return -1 (unlimited) for such rooms, regardless of the parent's actual configured maximum. Add HistoryStrategy.resolveEffective() to walk up the parent chain to the strategy whose settings are actually in effect, and use it in getMaxMessages() so the correct type and maxNumber are reported. --- .../openfire/muc/HistoryStrategy.java | 17 ++++++++++++++++- .../openfire/muc/MUCRoomHistory.java | 7 ++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java index 19afe906eb..552a8a98f9 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Jive Software, 2016-2025 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2004-2008 Jive Software, 2016-2026 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -321,6 +321,21 @@ public void purge() } } + /** + * Resolves this strategy to the one whose settings are actually in effect: if this strategy's type is + * {@link Type#defaulType}, walks up the parent chain until a strategy with a concrete type (or no parent) is found. + * Returns this instance if its type is not defaulType, or if it has no parent to defer to. + * + * @return The strategy instance whose type/maxNumber values should be treated as authoritative. + */ + HistoryStrategy resolveEffective() { + HistoryStrategy strategy = this; + while (strategy.type == Type.defaulType && strategy.parent != null) { + strategy = strategy.parent; + } + return strategy; + } + @Override public void writeExternal(ObjectOutput out) throws IOException { ExternalizableUtil.getInstance().writeSerializable(out, type); diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomHistory.java b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomHistory.java index 19b8e7af87..92ee7a3c8f 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomHistory.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomHistory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Jive Software, 2016-2025 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2004-2008 Jive Software, 2016-2026 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -324,8 +324,9 @@ public boolean isSubjectChangeRequest(Message message) { * @return The maximum number of historic messages to keep for this room, or -1. */ public int getMaxMessages() { - return switch (historyStrategy.getType()) { - case number -> historyStrategy.getMaxNumber(); + final HistoryStrategy effective = historyStrategy.resolveEffective(); + return switch (effective.getType()) { + case number -> effective.getMaxNumber(); case none -> 0; default -> -1; }; From 19247df92f6f2eb8b5369f1b6774a71cf4cc73e2 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Mon, 7 Sep 2026 21:03:45 +0100 Subject: [PATCH 2/3] OF-3364: Refactor some other strategy uses to prevent similar issues in the future Right now, none of the callers to addMessage or to isHistoryEnabled can have a deep hierarchy of HistoryStrategy. This refactoring uses the new implementation to ensure we can't slip the same class of bug as OF-3364 into the code again in the future. --- .../openfire/muc/HistoryStrategy.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java index 552a8a98f9..83d380d55f 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java @@ -205,16 +205,9 @@ public void addMessage(@Nonnull final Message... packets) } // get the conditions based on default or not - final Type strategyType; - final int strategyMaxNumber; - if (type == Type.defaulType && parent != null) { - strategyType = parent.getType(); - strategyMaxNumber = parent.getMaxNumber(); - } - else { - strategyType = type; - strategyMaxNumber = maxNumber; - } + final HistoryStrategy effective = resolveEffective(); + final Type strategyType = effective.getType(); + final int strategyMaxNumber = effective.getMaxNumber(); final Lock lock = MUC_HISTORY_CACHE.getLock(roomJID); lock.lock(); @@ -238,11 +231,7 @@ public void addMessage(@Nonnull final Message... packets) } boolean isHistoryEnabled() { - Type strategyType = type; - if (type == Type.defaulType && parent != null) { - strategyType = parent.getType(); - } - return strategyType != HistoryStrategy.Type.none; + return resolveEffective().getType() != Type.none; } /** From 00628634cf61da1241309e76fddefbf263ada7e1 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Mon, 7 Sep 2026 21:12:59 +0100 Subject: [PATCH 3/3] OF-3364: Fix an old typo in a variable name defaulType => defaultType --- .../jivesoftware/openfire/muc/HistoryStrategy.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java index 83d380d55f..04667df534 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/muc/HistoryStrategy.java @@ -124,7 +124,7 @@ public HistoryStrategy(JID roomJID, HistoryStrategy parentStrategy) { maxNumber = DEFAULT_MAX_NUMBER; } else { - type = Type.defaulType; + type = Type.defaultType; maxNumber = parent.getMaxNumber(); } } @@ -312,14 +312,14 @@ public void purge() /** * Resolves this strategy to the one whose settings are actually in effect: if this strategy's type is - * {@link Type#defaulType}, walks up the parent chain until a strategy with a concrete type (or no parent) is found. - * Returns this instance if its type is not defaulType, or if it has no parent to defer to. + * {@link Type#defaultType}, walks up the parent chain until a strategy with a concrete type (or no parent) is found. + * Returns this instance if its type is not defaultType, or if it has no parent to defer to. * * @return The strategy instance whose type/maxNumber values should be treated as authoritative. */ HistoryStrategy resolveEffective() { HistoryStrategy strategy = this; - while (strategy.type == Type.defaulType && strategy.parent != null) { + while (strategy.type == Type.defaultType && strategy.parent != null) { strategy = strategy.parent; } return strategy; @@ -387,7 +387,7 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept * Strategy type. */ public enum Type { - defaulType, none, all, number; + defaultType, none, all, number; } /** @@ -404,7 +404,7 @@ public void setTypeFromString(String typeName) { } catch (Exception e) { if (parent != null) { - type = Type.defaulType; + type = Type.defaultType; } else { type = Type.number;