-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add generic support for XEP-0128: Service Discovery Extensions #3138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Fishbowler
wants to merge
7
commits into
main
Choose a base branch
from
ext-info-extensions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,757
−92
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
f8d7250
Add ExtendedDiscoInfoProvider to provide generic support for XEP-0128
Fishbowler ad0b6ee
Extract Contact Addresses and Software Info to separate ExtendedDisco…
Fishbowler 39486dd
Update doap file to include XEP-0157 and XEP-0232
Fishbowler ffbb1a5
Move methods up to IQDiscoInfoHandler, and call from handleIQ
Fishbowler a8ec151
Migrate admin.disable-exposure from JiveGlobals to SystemProperty
Fishbowler 0367ec4
Add JavaDoc for the two extended disco info providers
Fishbowler 649ccf2
Remove a pointless test, and fix their docs
Fishbowler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
.../main/java/org/jivesoftware/openfire/disco/ContactAddressesExtendedDiscoInfoProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
Fishbowler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 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; | ||
| } | ||
| } | ||
66 changes: 66 additions & 0 deletions
66
xmppserver/src/main/java/org/jivesoftware/openfire/disco/ExtendedDiscoInfoProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
|
|
||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.