From 7b1d20f2043aeb7653a9ec630175208fec898a19 Mon Sep 17 00:00:00 2001 From: Shepard199 Date: Mon, 27 Jul 2026 20:48:59 +0300 Subject: [PATCH] Fix contact JID handling and roster fallback --- .../org/jivesoftware/spark/UserManager.java | 3 +++ .../jivesoftware/spark/ui/RosterDialog.java | 13 ++++++---- .../jivesoftware/spark/UserManagerTest.java | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/org/jivesoftware/spark/UserManagerTest.java diff --git a/core/src/main/java/org/jivesoftware/spark/UserManager.java b/core/src/main/java/org/jivesoftware/spark/UserManager.java index 86677b11d..5cd4dada9 100644 --- a/core/src/main/java/org/jivesoftware/spark/UserManager.java +++ b/core/src/main/java/org/jivesoftware/spark/UserManager.java @@ -280,6 +280,9 @@ public class UserManager { } String node = XmppStringUtils.parseLocalpart(jid); + if (node == null || node.isEmpty()) { + return jid; + } String restOfJID = jid.substring(node.length()); String builder = XmppStringUtils.escapeLocalpart(node) + restOfJID; return builder; diff --git a/core/src/main/java/org/jivesoftware/spark/ui/RosterDialog.java b/core/src/main/java/org/jivesoftware/spark/ui/RosterDialog.java index 82b898b4e..fd72f445c 100644 --- a/core/src/main/java/org/jivesoftware/spark/ui/RosterDialog.java +++ b/core/src/main/java/org/jivesoftware/spark/ui/RosterDialog.java @@ -540,11 +540,12 @@ public class RosterDialog implements ActionListener { if (roster.isSubscriptionPreApprovalSupported()) { try { roster.preApproveAndCreateEntry(contactJid, displayName, parentNames); - } catch (SmackException.FeatureNotSupportedException ignored) { + return; + } catch (SmackException.FeatureNotSupportedException e) { + Log.debug("Roster subscription pre-approval is unavailable; using a regular subscription request."); } - } else { - roster.createItemAndRequestSubscription(contactJid, displayName, parentNames); } + roster.createItemAndRequestSubscription(contactJid, displayName, parentNames); } public List getAccounts() { @@ -571,7 +572,7 @@ public class RosterDialog implements ActionListener { return; } - String contact = UserManager.escapeJID(jid); + String contact = jid; String nickname = nicknameField.getText(); String group = (String) groupBox.getSelectedItem(); @@ -592,6 +593,10 @@ public class RosterDialog implements ActionListener { } } + // Escape only after a complete JID has been constructed. Escaping a bare + // username first causes XmppStringUtils.parseLocalpart() to return null. + contact = UserManager.escapeJID(contact); + if (!ModelUtil.hasLength(nickname) && ModelUtil.hasLength(contact)) { // Try to load nickname from VCard try { diff --git a/core/src/test/java/org/jivesoftware/spark/UserManagerTest.java b/core/src/test/java/org/jivesoftware/spark/UserManagerTest.java new file mode 100644 index 000000000..fc3685bff --- /dev/null +++ b/core/src/test/java/org/jivesoftware/spark/UserManagerTest.java @@ -0,0 +1,24 @@ +package org.jivesoftware.spark; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UserManagerTest { + + @Test + public void escapeJidKeepsUsernameWithoutDomain() { + assertEquals("alice", UserManager.escapeJID("alice")); + } + + @Test + public void escapeJidEscapesCompleteJidLocalpart() { + assertEquals("alice\\20smith@example.org", UserManager.escapeJID("alice smith@example.org")); + } + + @Test + public void escapeJidKeepsNull() { + assertNull(UserManager.escapeJID(null)); + } +}