%@ page contentType="text/html; charset=UTF-8" %>
<%--
-
- Copyright (C) 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.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--%>
<%@ page errorPage="error.jsp" %>
<%@ page import="org.jivesoftware.openfire.net.DNSUtil" %>
<%@ page import="org.jivesoftware.openfire.net.SrvRecord" %>
<%@ page import="org.jivesoftware.util.CookieUtils" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="java.util.*" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<% webManager.init(request, response, session, application, out ); %>
<%
final Map errors = new LinkedHashMap<>();
final boolean save = ParamUtils.getParameter(request, "save") != null;
final String deletePattern = ParamUtils.getParameter(request, "deletePattern", true);
final String editPattern = ParamUtils.getParameter(request, "editPattern", true);
String domainPattern = ParamUtils.getParameter(request, "domainPattern", true);
String targetHost = ParamUtils.getParameter(request, "targetHost", true);
String targetPort = ParamUtils.getParameter(request, "targetPort", true);
final boolean success = ParamUtils.getBooleanParameter(request, "success");
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (save || deletePattern != null) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
errors.put("csrf", null);
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
if (deletePattern != null && errors.isEmpty()) {
final String normalizedPattern = deletePattern.trim().toLowerCase(Locale.ROOT);
final Map current = DNSUtil.getDnsOverride();
if (current != null && current.containsKey(normalizedPattern)) {
final Map updated = new HashMap<>(current);
updated.remove(normalizedPattern);
// Pass null (not an empty map) so DNSUtil removes the property entirely.
DNSUtil.setDnsOverride(updated.isEmpty() ? null : updated);
webManager.logEvent("Updated DNS override configuration.",
"Removed DNS override for pattern '" + normalizedPattern + "'.");
}
response.sendRedirect("dns-overrides.jsp?success=true");
return;
}
if (save && errors.isEmpty()) {
// Validate that all required fields were submitted.
if (domainPattern == null || domainPattern.trim().isEmpty()) {
errors.put("domainPattern.required", null);
}
if (targetHost == null || targetHost.trim().isEmpty()) {
errors.put("targetHost.required", null);
}
if (targetPort == null || targetPort.trim().isEmpty()) {
errors.put("targetPort.required", null);
}
// Protect DNSUtil's property encoding format from delimiter collisions in user-provided values.
final String persistenceDelimiters = "{},:";
if (!errors.containsKey("domainPattern.required") && domainPattern != null) {
final String trimmedDomainPattern = domainPattern.trim();
for (char delimiter : persistenceDelimiters.toCharArray()) {
if (trimmedDomainPattern.indexOf(delimiter) >= 0) {
errors.put("domainPattern.invalid-delimiters", null);
break;
}
}
}
if (!errors.containsKey("targetHost.required") && targetHost != null) {
final String trimmedTargetHost = targetHost.trim();
for (char delimiter : "{},".toCharArray()) {
if (trimmedTargetHost.indexOf(delimiter) >= 0) {
errors.put("targetHost.invalid-delimiters", null);
break;
}
}
}
// Parse and range-check the port number (must be 1–65535).
Integer parsedPort = null;
if (!errors.containsKey("targetPort.required") && targetPort != null) {
try {
parsedPort = Integer.valueOf(targetPort.trim());
if (parsedPort < 1 || parsedPort > 65535) {
errors.put("targetPort.invalid", targetPort);
parsedPort = null;
}
} catch (NumberFormatException e) {
errors.put("targetPort.invalid", targetPort);
}
}
if (errors.isEmpty()) {
// Normalise the key to lowercase, copy the existing map, add/replace the entry.
final String normalizedPattern = domainPattern.trim().toLowerCase(Locale.ROOT);
final Map current = DNSUtil.getDnsOverride();
final Map updated = (current != null) ? new HashMap<>(current) : new HashMap<>();
updated.put(normalizedPattern, new SrvRecord(targetHost.trim(), parsedPort, false));
DNSUtil.setDnsOverride(updated);
webManager.logEvent("Updated DNS override configuration.", "Configured DNS override '" + normalizedPattern + "' to '" + targetHost.trim() + ":" + parsedPort + "'.");
response.sendRedirect("dns-overrides.jsp?success=true");
return;
}
}
// Retrieve current DNS overrides as an ordered snapshot (matching DNSUtil precedence rules).
final Map configuredOverrides = DNSUtil.getDnsOverride();
final List> overrides = DNSUtil.getDnsOverrideEntriesByPrecedence();
/*
* Pre-fill the add/update form when the operator clicked the edit icon in the table.
* The pattern from the URL parameter is lowercased and used to look up the existing entry;
* if found, its values are placed in the form fields so the operator can modify and re-save.
*/
if ((domainPattern == null || domainPattern.trim().isEmpty()) && editPattern != null && configuredOverrides != null) {
final String normalizedEditPattern = editPattern.trim().toLowerCase(Locale.ROOT);
final SrvRecord existingOverride = configuredOverrides.get(normalizedEditPattern);
if (existingOverride != null) {
domainPattern = normalizedEditPattern;
targetHost = existingOverride.getHostname();
targetPort = Integer.toString(existingOverride.getPort());
}
}
// Expose all computed values to JSTL/EL for rendering in the HTML section below.
pageContext.setAttribute("errors", errors);
pageContext.setAttribute("success", errors.isEmpty() && success);
pageContext.setAttribute("overrides", overrides);
pageContext.setAttribute("domainPattern", domainPattern == null ? "" : domainPattern);
pageContext.setAttribute("targetHost", targetHost == null ? "" : targetHost);
pageContext.setAttribute("targetPort", targetPort == null ? "" : targetPort);
%>