Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion documentation/openfire.doap
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0128.html"/>
<xmpp:note xml:lang='en'>Provided by the 'MUC Service Discovery Extensions' plugin</xmpp:note>
</xmpp:SupportedXep>
</implements>
<implements>
Expand Down Expand Up @@ -247,6 +246,11 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0156.html"/>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0157.html"/>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0160.html"/>
Expand Down Expand Up @@ -324,6 +328,11 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0223.html"/>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0232.html"/>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0233.html"/>
Expand Down
1 change: 1 addition & 0 deletions i18n/src/main/resources/openfire_i18n.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,7 @@ system_property.hybridUserProvider.secondaryProvider.config=Configuration value
system_property.hybridUserProvider.tertiaryProvider.className=The third class the HybridUserProvider should use to get users from.
system_property.hybridUserProvider.tertiaryProvider.config=Configuration value for the third class used by the HybridUserProvider.
system_property.admin.authorizedJIDs=The bare JID of every admin user for the DefaultAdminProvider
system_property.admin.disable-exposure=When set to true, suppresses extended service discovery information (contact addresses, software versions) that may expose administrative details. Use this for enhanced privacy/security when you don't want to disclose server configuration details.
system_property.xmpp.auth.ssl.context_protocol=The TLS protocol to use for encryption context initialization, overriding the Java default.
system_property.xmpp.parser.buffer.size=Maximum size of an XMPP stanza. Larger stanzas will cause a connection to be closed.
system_property.xmpp.auth.ssl.enforce_sni=Controls if the server enforces the use of SNI (Server Name Indication) when clients connect using TLS.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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.
*/

package org.jivesoftware.openfire.disco;

import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.UserManager;
import org.xmpp.forms.DataForm;
import org.xmpp.forms.FormField;
import org.xmpp.packet.JID;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
* Provides XEP-0157 Contact Addresses for XMPP Services via service discovery.
* <p>
* This provider returns a data form containing administrative contact addresses for the server,
* including XMPP addresses for administrators and their email addresses when available.
* </p>
* <p>
* The contact information is only returned when:
* <ul>
* <li>The {@code admin.disable-exposure} property is not set to true</li>
* <li>The request is for the server domain (name is null or matches the server domain)</li>
* <li>No specific node is requested (node is null)</li>
* <li>At least one administrator is configured</li>
* </ul>
* </p>
*
* @see <a href="https://xmpp.org/extensions/xep-0157.html">XEP-0157: Contact Addresses for XMPP Services</a>
*/
public class ContactAddressesExtendedDiscoInfoProvider implements ExtendedDiscoInfoProvider {

/**
* Returns XEP-0157 Contact Addresses data form for the server when appropriate conditions are met.
* <p>
* This implementation only returns contact information for service-level disco#info queries
* targeting the server's main domain (not subdomains like MUC or PubSub). The returned data form
* contains XMPP addresses of configured administrators and their email addresses when available.
* </p>
* <p>
* Returns an empty set when:
* <ul>
* <li>Administrative exposure is disabled ({@link IQDiscoInfoHandler#DISABLE_EXPOSURE} is true)</li>
* <li>A specific disco node is requested (only responds to node-less queries)</li>
* <li>The domain is not the server's main domain (e.g., MUC or PubSub subdomains)</li>
* <li>A specific user/resource is targeted (only responds to service-level queries where name is null)</li>
* <li>No administrators are configured</li>
* </ul>
* </p>
*
* @param domain the domain of the target JID (e.g., "localhost", "conference.localhost")
* @param name the node part of the target JID (null for service-level queries)
* @param node the requested disco node parameter (null if not specified)
* @param senderJID the JID of the entity that sent the disco#info request
* @return A set containing a single XEP-0157 data form with contact addresses, or an empty set if conditions are not met
* @see <a href="https://xmpp.org/extensions/xep-0157.html">XEP-0157: Contact Addresses for XMPP Services</a>
*/
@Override
public Set<DataForm> getExtendedInfos(String domain, String name, String node, JID senderJID) {
// Return empty set if admin exposure is disabled
if (IQDiscoInfoHandler.DISABLE_EXPOSURE.getValue()) {
return Collections.emptySet();
}

// Only respond for server-level requests (no node)
if (node != null) {
return Collections.emptySet();
}

// Only respond for server domain (not MUC, PubSub, etc.)
final String serverDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
if (!serverDomain.equals(domain)) {
return Collections.emptySet();
}

// Only respond for service-level queries (name == null)
if (name != null) {
return Collections.emptySet();
}

// Get admins and return empty if none
final Collection<JID> admins = XMPPServer.getInstance().getAdmins();
if (admins == null || admins.isEmpty()) {
return Collections.emptySet();
}

// Build XEP-0157 form
final DataForm dataForm = new DataForm(DataForm.Type.result);

final FormField fieldType = dataForm.addField();
fieldType.setVariable("FORM_TYPE");
fieldType.setType(FormField.Type.hidden);
fieldType.addValue("http://jabber.org/network/serverinfo");

final FormField fieldAdminAddresses = dataForm.addField();
fieldAdminAddresses.setVariable("admin-addresses");
fieldAdminAddresses.setType(FormField.Type.list_multi);

final UserManager userManager = UserManager.getInstance();
for (final JID admin : admins) {
fieldAdminAddresses.addValue("xmpp:" + admin.asBareJID());
if (admin.getDomain().equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
try {
final String email = userManager.getUser(admin.getNode()).getEmail();
if (email != null && !email.trim().isEmpty()) {
fieldAdminAddresses.addValue("mailto:" + email);
}
} catch (Exception e) {
// User not found or other error - skip email for this admin
continue;
}
}
}

final Set<DataForm> dataForms = new HashSet<>();
dataForms.add(dataForm);
return dataForms;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.
*/

package org.jivesoftware.openfire.disco;

import org.xmpp.forms.DataForm;
import org.xmpp.packet.JID;

import java.util.Set;

/**
* Provides extended service discovery information using XEP-0128 data form extensions.
* <p>
* Implementations of this interface can contribute additional XEP-0004 data forms to disco#info responses,
* enabling plugin-controlled additions to service discovery as defined by XEP-0128.
* This supports use cases such as:
* <ul>
* <li>Adding custom fields to existing forms (e.g. what the MUC Extended Info plugin does)</li>
* <li>Providing entirely new data forms with custom FORM_TYPE values (e.g. XEP-0232: Software Information, XEP-0504: Data Policy)</li>
* </ul>
* </p>
*
* When multiple providers return forms with the same FORM_TYPE, the forms are merged by combining
* their fields. However, <b>each field must be unique</b> within a form type. If two providers contribute
* fields with the same field name to the same FORM_TYPE, this is treated as a configuration error. The duplicate
* provider's entire contribution is skipped and a warning is logged. Other providers continue to be processed
* normally.
*
* Implementations may wish to check {@link IQDiscoInfoHandler#DISABLE_EXPOSURE} and return an empty set
* when it is true, to respect privacy configurations that suppress exposure of administrative details.
*
* @see <a href="https://xmpp.org/extensions/xep-0004.html">XEP-0004: Data Forms</a>
* @see <a href="https://xmpp.org/extensions/xep-0128.html">XEP-0128: Service Discovery Extensions</a>
* @see <a href="https://xmpp.org/extensions/xep-0504.html">XEP-0504: Data Policy</a>
*/
public interface ExtendedDiscoInfoProvider {

/**
* Returns a collection of data forms with extended information about the entity.
* <p>
* The returned Set may be empty but should not be null. It may be immutable.
* Forms should include a FORM_TYPE field to enable proper merging.
* </p>
*
* @param domain the domain of the target JID (e.g., "localhost", "conference.localhost", "pubsub.localhost")
* @param name the node part of the target JID (null for service-level queries)
* @param node the requested disco node parameter (null if not specified)
* @param senderJID the XMPPAddress of user that sent the disco info request
* @return A Set of data forms (possibly empty, never null). May be immutable.
*/
Set<DataForm> getExtendedInfos(String domain, String name, String node, JID senderJID);

}
Loading
Loading