<%@ 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); %> <fmt:message key="dns.overrides.title"/>

"><fmt:message key="> " onclick="return confirm(this.getAttribute('data-confirm'));" title=""><fmt:message key=">

" required ${not empty errors['domainPattern.required'] or not empty errors['domainPattern.invalid-delimiters'] ? 'autofocus="autofocus" style="background-color: #ffdddd;"' : ''}>
" required ${not empty errors['targetHost.required'] or not empty errors['targetHost.invalid-delimiters'] ? 'autofocus="autofocus" style="background-color: #ffdddd;"' : ''}>
">