From 0e1109e600be997004fef79f8082751677cccf99 Mon Sep 17 00:00:00 2001 From: Milan Tyagi Date: Thu, 4 Jun 2026 14:27:03 +0530 Subject: [PATCH 1/3] OF-31: Preserve conference service context in room creation flow When creating a new MUC room from a service-specific administration page, the room creation form incorrectly defaulted to the alphabetically first conference service. Extract the active service context from the originating request and pass it through the room creation flow so that the selected conference service is pre-populated when available. Falls back to the existing default behavior when no valid service context can be determined. --- .../src/main/webapp/muc-room-create.jsp | 52 +++++++++++++++++-- .../src/main/webapp/muc-room-edit-form.jsp | 36 ++++++++++--- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/xmppserver/src/main/webapp/muc-room-create.jsp b/xmppserver/src/main/webapp/muc-room-create.jsp index 0d62c2c44e..c12d94dcba 100644 --- a/xmppserver/src/main/webapp/muc-room-create.jsp +++ b/xmppserver/src/main/webapp/muc-room-create.jsp @@ -1,7 +1,7 @@ <%@ page contentType="text/html; charset=UTF-8" %> <%-- - - - Copyright (C) 2004-2008 Jive Software, 2017-2018 Ignite Realtime Foundation. All rights reserved. + - Copyright (C) 2004-2008 Jive Software, 2017-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. @@ -18,6 +18,52 @@ --%> <% - // Redirect to muc-room-edit-form and set that a room will be created - response.sendRedirect("muc-room-edit-form.jsp?create=true"); + // OF-31: To select the current conference service automatically on room creation, + // we extract the active service context from the HTTP Referer header. This allows us + // to preserve context from top-level sidebar navigation links (which are statically + // defined in admin-sidebar.xml and do not support dynamic parameters) without having + // to introduce session state or perform intrusive layout changes. + // + // The extraction is done here in the redirector page (muc-room-create.jsp), but the + // validation is kept inside muc-room-edit-form.jsp to preserve clean separation of + // concerns and fallback behaviors. + String referrer = request.getHeader("Referer"); + String serviceParam = ""; + if (referrer != null) { + if (referrer.contains("mucname=")) { + int idx = referrer.indexOf("mucname="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); + serviceParam = "&mucName=" + val; + } else if (referrer.contains("mucName=")) { + int idx = referrer.indexOf("mucName="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); + serviceParam = "&mucName=" + val; + } else if (referrer.contains("roomJID=")) { + int idx = referrer.indexOf("roomJID="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); + try { + val = java.net.URLDecoder.decode(val, java.nio.charset.StandardCharsets.UTF_8.name()); + // roomJID is like room@service.domain. Extract the domain part. + org.xmpp.packet.JID jid = new org.xmpp.packet.JID(val); + serviceParam = "&mucName=" + java.net.URLEncoder.encode(jid.getDomain(), java.nio.charset.StandardCharsets.UTF_8.name()); + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } + } + } + // Redirect to muc-room-edit-form and set that a room will be created, forwarding the active service parameter if found + response.sendRedirect("muc-room-edit-form.jsp?create=true" + serviceParam); %> + diff --git a/xmppserver/src/main/webapp/muc-room-edit-form.jsp b/xmppserver/src/main/webapp/muc-room-edit-form.jsp index 5681cb1569..3a76b58f25 100644 --- a/xmppserver/src/main/webapp/muc-room-edit-form.jsp +++ b/xmppserver/src/main/webapp/muc-room-edit-form.jsp @@ -1,7 +1,7 @@ <%@ page contentType="text/html; charset=UTF-8" %> <%-- - - - Copyright (C) 2004-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved. + - Copyright (C) 2004-2008 Jive Software, 2017-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. @@ -54,6 +54,9 @@ boolean clearchatsuccess = ParamUtils.getBooleanParameter(request,"clearchatsuccess"); String roomName = ParamUtils.getParameter(request,"roomName"); String mucName = ParamUtils.getParameter(request,"mucName"); + if (mucName == null) { + mucName = ParamUtils.getParameter(request,"mucname"); + } String roomJIDStr = ParamUtils.getParameter(request,"roomJID"); JID roomJID = null; if (roomName != null && mucName != null) { @@ -306,11 +309,32 @@ } else { if (create) { - // Before a selection for a service has been made (which is part of the room creation process in cases where - // more than one service exists) it's impossible to predict what service-specific configuration to use. To prevent - // having the user to go through a second step, we'll use the first available service. Given that having more than one - // service is a very uncommon scenario, this is an acceptable shortcut. - final String serviceName = webManager.getMultiUserChatManager().getMultiUserChatServices().iterator().next().getServiceName(); + // OF-31: Retrieve and validate the candidate service context parameter (mucName). + // Fall back to the alphabetically first service if missing, invalid, or does not exist. + // This ensures backwards compatibility and graceful fallback when accessed directly. + String resolvedServiceName = null; + if (mucName != null) { + if (webManager.getMultiUserChatManager().isServiceRegistered(mucName)) { + resolvedServiceName = mucName; + } else { + try { + org.jivesoftware.openfire.muc.MultiUserChatService service = webManager.getMultiUserChatManager().getMultiUserChatService(new JID(null, mucName, null)); + if (service != null) { + resolvedServiceName = service.getServiceName(); + } + } catch (Exception e) { + // ignore + } + } + } + if (resolvedServiceName == null) { + resolvedServiceName = webManager.getMultiUserChatManager().getMultiUserChatServices().iterator().next().getServiceName(); + } + final String serviceName = resolvedServiceName; + org.jivesoftware.openfire.muc.MultiUserChatService resolvedService = webManager.getMultiUserChatManager().getMultiUserChatService(serviceName); + if (resolvedService != null) { + mucName = resolvedService.getServiceDomain(); + } maxUsers = MUCPersistenceManager.getProperty(serviceName, "room.maxUsers", "30"); broadcastModerator = MUCPersistenceManager.getBooleanProperty(serviceName, "room.broadcastModerator", true); broadcastParticipant = MUCPersistenceManager.getBooleanProperty(serviceName, "room.broadcastParticipant", true); From d1a524455fb08c8bcadc9838258bb1972b6b9f04 Mon Sep 17 00:00:00 2001 From: Milan Tyagi Date: Thu, 4 Jun 2026 22:50:42 +0530 Subject: [PATCH 2/3] OF-31: Normalize mucName values extracted from Referer Apply URL decode/encode normalization to mucname and mucName parameters extracted from the Referer header. This aligns their handling with the existing roomJID path and ensures consistent processing of URL-encoded values before forwarding them to the room creation form. --- .../src/main/webapp/muc-room-create.jsp | 106 +++++++++++++----- 1 file changed, 75 insertions(+), 31 deletions(-) diff --git a/xmppserver/src/main/webapp/muc-room-create.jsp b/xmppserver/src/main/webapp/muc-room-create.jsp index c12d94dcba..a10b1170c5 100644 --- a/xmppserver/src/main/webapp/muc-room-create.jsp +++ b/xmppserver/src/main/webapp/muc-room-create.jsp @@ -30,39 +30,83 @@ String referrer = request.getHeader("Referer"); String serviceParam = ""; if (referrer != null) { - if (referrer.contains("mucname=")) { - int idx = referrer.indexOf("mucname="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } - String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); - serviceParam = "&mucName=" + val; - } else if (referrer.contains("mucName=")) { - int idx = referrer.indexOf("mucName="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } - String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); - serviceParam = "&mucName=" + val; - } else if (referrer.contains("roomJID=")) { - int idx = referrer.indexOf("roomJID="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } - String val = endIdx != -1 ? referrer.substring(idx + 8, endIdx) : referrer.substring(idx + 8); - try { - val = java.net.URLDecoder.decode(val, java.nio.charset.StandardCharsets.UTF_8.name()); - // roomJID is like room@service.domain. Extract the domain part. - org.xmpp.packet.JID jid = new org.xmpp.packet.JID(val); - serviceParam = "&mucName=" + java.net.URLEncoder.encode(jid.getDomain(), java.nio.charset.StandardCharsets.UTF_8.name()); - } catch (Exception e) { - // Ignore parsing errors and let validation handle fallbacks - } + if (referrer.contains("mucname=")) { + int idx = referrer.indexOf("mucname="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + + String val = endIdx != -1 + ? referrer.substring(idx + 8, endIdx) + : referrer.substring(idx + 8); + + try { + String decodedVal = java.net.URLDecoder.decode( + val, + java.nio.charset.StandardCharsets.UTF_8.name() + ); + String encodedVal = java.net.URLEncoder.encode( + decodedVal, + java.nio.charset.StandardCharsets.UTF_8.name() + ); + serviceParam = "&mucName=" + encodedVal; + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } + } else if (referrer.contains("mucName=")) { + int idx = referrer.indexOf("mucName="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + + String val = endIdx != -1 + ? referrer.substring(idx + 8, endIdx) + : referrer.substring(idx + 8); + + try { + String decodedVal = java.net.URLDecoder.decode( + val, + java.nio.charset.StandardCharsets.UTF_8.name() + ); + String encodedVal = java.net.URLEncoder.encode( + decodedVal, + java.nio.charset.StandardCharsets.UTF_8.name() + ); + serviceParam = "&mucName=" + encodedVal; + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } + } else if (referrer.contains("roomJID=")) { + int idx = referrer.indexOf("roomJID="); + int endIdx = referrer.indexOf("&", idx); + if (endIdx == -1) { + endIdx = referrer.indexOf("#", idx); + } + + String val = endIdx != -1 + ? referrer.substring(idx + 8, endIdx) + : referrer.substring(idx + 8); + + try { + val = java.net.URLDecoder.decode( + val, + java.nio.charset.StandardCharsets.UTF_8.name() + ); + + org.xmpp.packet.JID jid = new org.xmpp.packet.JID(val); + + serviceParam = "&mucName=" + + java.net.URLEncoder.encode( + jid.getDomain(), + java.nio.charset.StandardCharsets.UTF_8.name() + ); + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks } } +} // Redirect to muc-room-edit-form and set that a room will be created, forwarding the active service parameter if found response.sendRedirect("muc-room-edit-form.jsp?create=true" + serviceParam); %> From 9d3a3b729b33bc7c7bf4ee4d32fbac46498e1bda Mon Sep 17 00:00:00 2001 From: Milan Tyagi Date: Fri, 5 Jun 2026 10:08:04 +0530 Subject: [PATCH 3/3] OF-31: Address review feedback for Referer-based service resolution - Normalize mucname and mucName values using URL decode/encode to ensure consistent handling of URL-encoded parameters. - Replace substring-based Referer parsing with key-bound query parameter extraction to prevent false matches from similarly named parameters. - Use regex-based parameter extraction for mucname, mucName, and roomJID values. - Simplify URL encoding/decoding usage through imports. - Preserve existing fallback behavior when service context cannot be resolved. --- .../src/main/webapp/muc-room-create.jsp | 132 ++++++++---------- 1 file changed, 62 insertions(+), 70 deletions(-) diff --git a/xmppserver/src/main/webapp/muc-room-create.jsp b/xmppserver/src/main/webapp/muc-room-create.jsp index a10b1170c5..29e37cf929 100644 --- a/xmppserver/src/main/webapp/muc-room-create.jsp +++ b/xmppserver/src/main/webapp/muc-room-create.jsp @@ -16,7 +16,11 @@ - limitations under the License. --%> - +<%@ page import="java.util.function.BiFunction" %> +<%@ page import="java.util.regex.Pattern" %> +<%@ page import="java.util.regex.Matcher" %> +<%@ page import="static java.nio.charset.StandardCharsets.UTF_8" %> +<%@ page import="java.net.URLEncoder,java.net.URLDecoder" %> <% // OF-31: To select the current conference service automatically on room creation, // we extract the active service context from the HTTP Referer header. This allows us @@ -27,87 +31,75 @@ // The extraction is done here in the redirector page (muc-room-create.jsp), but the // validation is kept inside muc-room-edit-form.jsp to preserve clean separation of // concerns and fallback behaviors. + String referrer = request.getHeader("Referer"); String serviceParam = ""; - if (referrer != null) { - if (referrer.contains("mucname=")) { - int idx = referrer.indexOf("mucname="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } - String val = endIdx != -1 - ? referrer.substring(idx + 8, endIdx) - : referrer.substring(idx + 8); + BiFunction extractParam = (url, key) -> { + Matcher m = Pattern + .compile("(?:[?&])" + Pattern.quote(key) + "=([^&#]*)") + .matcher(url); - try { - String decodedVal = java.net.URLDecoder.decode( - val, - java.nio.charset.StandardCharsets.UTF_8.name() - ); - String encodedVal = java.net.URLEncoder.encode( - decodedVal, - java.nio.charset.StandardCharsets.UTF_8.name() - ); - serviceParam = "&mucName=" + encodedVal; - } catch (Exception e) { - // Ignore parsing errors and let validation handle fallbacks - } - } else if (referrer.contains("mucName=")) { - int idx = referrer.indexOf("mucName="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } + return m.find() ? m.group(1) : null; + }; - String val = endIdx != -1 - ? referrer.substring(idx + 8, endIdx) - : referrer.substring(idx + 8); + if (referrer != null) { + String val; - try { - String decodedVal = java.net.URLDecoder.decode( - val, - java.nio.charset.StandardCharsets.UTF_8.name() - ); - String encodedVal = java.net.URLEncoder.encode( - decodedVal, - java.nio.charset.StandardCharsets.UTF_8.name() - ); - serviceParam = "&mucName=" + encodedVal; - } catch (Exception e) { - // Ignore parsing errors and let validation handle fallbacks - } - } else if (referrer.contains("roomJID=")) { - int idx = referrer.indexOf("roomJID="); - int endIdx = referrer.indexOf("&", idx); - if (endIdx == -1) { - endIdx = referrer.indexOf("#", idx); - } + if ((val = extractParam.apply(referrer, "mucname")) != null) { + try { + String decodedVal = URLDecoder.decode( + val, + UTF_8.name() + ); - String val = endIdx != -1 - ? referrer.substring(idx + 8, endIdx) - : referrer.substring(idx + 8); + String encodedVal = URLEncoder.encode( + decodedVal, + UTF_8.name() + ); - try { - val = java.net.URLDecoder.decode( - val, - java.nio.charset.StandardCharsets.UTF_8.name() - ); + serviceParam = "&mucName=" + encodedVal; + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } + } else if ((val = extractParam.apply(referrer, "mucName")) != null) { + try { + String decodedVal = URLDecoder.decode( + val, + UTF_8.name() + ); - org.xmpp.packet.JID jid = new org.xmpp.packet.JID(val); + String encodedVal = URLEncoder.encode( + decodedVal, + UTF_8.name() + ); - serviceParam = "&mucName=" + - java.net.URLEncoder.encode( - jid.getDomain(), - java.nio.charset.StandardCharsets.UTF_8.name() + serviceParam = "&mucName=" + encodedVal; + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } + } else if ((val = extractParam.apply(referrer, "roomJID")) != null) { + try { + val = URLDecoder.decode( + val, + UTF_8.name() ); - } catch (Exception e) { - // Ignore parsing errors and let validation handle fallbacks + + org.xmpp.packet.JID jid = new org.xmpp.packet.JID(val); + + serviceParam = "&mucName=" + + URLEncoder.encode( + jid.getDomain(), + UTF_8.name() + ); + } catch (Exception e) { + // Ignore parsing errors and let validation handle fallbacks + } } } -} - // Redirect to muc-room-edit-form and set that a room will be created, forwarding the active service parameter if found - response.sendRedirect("muc-room-edit-form.jsp?create=true" + serviceParam); + + response.sendRedirect( + "muc-room-edit-form.jsp?create=true" + serviceParam + ); %>