From 3fc834c0674960b416d9adb270ed29ce500b2379 Mon Sep 17 00:00:00 2001 From: Sam Whited Date: Tue, 2 Dec 2014 09:16:09 -0500 Subject: Fix RFC 6122 implementation JID resourceparts should be able to contain "@" and "/" characters --- src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index ebf8a6ed..c156b8b6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -68,8 +68,9 @@ public final class Jid { if (jid.isEmpty() || jid.length() > 3071) { throw new InvalidJidException(InvalidJidException.INVALID_LENGTH); } - if (atCount > 1 || slashCount > 1 || - jid.startsWith("@") || jid.endsWith("@") || + + // Go ahead and check if the localpart or resourcepart is empty. + if (jid.startsWith("@") || jid.endsWith("@") || jid.startsWith("/") || jid.endsWith("/")) { throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); } @@ -77,7 +78,7 @@ public final class Jid { String finaljid; final int domainpartStart; - if (atCount == 1) { + if (atCount >= 1) { final int atLoc = jid.indexOf("@"); final String lp = jid.substring(0, atLoc); try { @@ -97,7 +98,7 @@ public final class Jid { } final String dp; - if (slashCount == 1) { + if (slashCount >= 1) { final int slashLoc = jid.indexOf("/"); final String rp = jid.substring(slashLoc + 1, jid.length()); try { -- cgit v1.2.3 From 4f9dd824577063eb25962d73a2c0069b31c92036 Mon Sep 17 00:00:00 2001 From: Sam Whited Date: Tue, 2 Dec 2014 10:09:31 -0500 Subject: "example.net/@/" should be a valid JID. Retab JID file --- .../java/eu/siacs/conversations/xmpp/jid/Jid.java | 330 +++++++++++---------- 1 file changed, 166 insertions(+), 164 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index c156b8b6..cb37bde3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -12,25 +12,25 @@ import gnu.inet.encoding.StringprepException; */ public final class Jid { - private final String localpart; - private final String domainpart; - private final String resourcepart; + private final String localpart; + private final String domainpart; + private final String resourcepart; - // It's much more efficient to store the ful JID as well as the parts instead of figuring them - // all out every time (since some characters are displayed but aren't used for comparisons). - private final String displayjid; + // It's much more efficient to store the ful JID as well as the parts instead of figuring them + // all out every time (since some characters are displayed but aren't used for comparisons). + private final String displayjid; - public String getLocalpart() { - return localpart; - } + public String getLocalpart() { + return localpart; + } - public String getDomainpart() { - return IDN.toUnicode(domainpart); - } + public String getDomainpart() { + return IDN.toUnicode(domainpart); + } - public String getResourcepart() { - return resourcepart; - } + public String getResourcepart() { + return resourcepart; + } public static Jid fromSessionID(SessionID id) throws InvalidJidException{ if (id.getUserID().isEmpty()) { @@ -40,153 +40,155 @@ public final class Jid { } } - public static Jid fromString(final String jid) throws InvalidJidException { - return new Jid(jid); - } - - public static Jid fromParts(final String localpart, - final String domainpart, - final String resourcepart) throws InvalidJidException { - String out; - if (localpart == null || localpart.isEmpty()) { - out = domainpart; - } else { - out = localpart + "@" + domainpart; - } - if (resourcepart != null && !resourcepart.isEmpty()) { - out = out + "/" + resourcepart; - } - return new Jid(out); - } - - private Jid(final String jid) throws InvalidJidException { - // Hackish Android way to count the number of chars in a string... should work everywhere. - final int atCount = jid.length() - jid.replace("@", "").length(); - final int slashCount = jid.length() - jid.replace("/", "").length(); - - // Throw an error if there's anything obvious wrong with the JID... - if (jid.isEmpty() || jid.length() > 3071) { - throw new InvalidJidException(InvalidJidException.INVALID_LENGTH); - } - - // Go ahead and check if the localpart or resourcepart is empty. - if (jid.startsWith("@") || jid.endsWith("@") || - jid.startsWith("/") || jid.endsWith("/")) { - throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); - } - - String finaljid; - - final int domainpartStart; - if (atCount >= 1) { - final int atLoc = jid.indexOf("@"); - final String lp = jid.substring(0, atLoc); - try { - localpart = Stringprep.nodeprep(lp); - } catch (final StringprepException e) { - throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); - } - if (localpart.isEmpty() || localpart.length() > 1023) { - throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); - } - domainpartStart = atLoc + 1; - finaljid = lp + "@"; - } else { - localpart = ""; - finaljid = ""; - domainpartStart = 0; - } - - final String dp; - if (slashCount >= 1) { - final int slashLoc = jid.indexOf("/"); - final String rp = jid.substring(slashLoc + 1, jid.length()); - try { - resourcepart = Stringprep.resourceprep(rp); - } catch (final StringprepException e) { - throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); - } - if (resourcepart.isEmpty() || resourcepart.length() > 1023) { - throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); - } - dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES); - finaljid = finaljid + dp + "/" + rp; - } else { - resourcepart = ""; - dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()), - IDN.USE_STD3_ASCII_RULES); - finaljid = finaljid + dp; - } - - // Remove trailing "." before storing the domain part. - if (dp.endsWith(".")) { - try { - domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES); - } catch (final IllegalArgumentException e) { - throw new InvalidJidException(e); - } - } else { - try { - domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES); - } catch (final IllegalArgumentException e) { - throw new InvalidJidException(e); - } - } - - // TODO: Find a proper domain validation library; validate individual parts, separators, etc. - if (domainpart.isEmpty() || domainpart.length() > 1023) { - throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); - } - - this.displayjid = finaljid; - } - - public Jid toBareJid() { - try { - return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, ""); - } catch (final InvalidJidException e) { - // This should never happen. - return null; - } - } - - public Jid toDomainJid() { - try { - return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart()); - } catch (final InvalidJidException e) { - // This should never happen. - return null; - } - } - - @Override - public String toString() { - return displayjid; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final Jid jid = (Jid) o; - - return jid.hashCode() == this.hashCode(); - } - - @Override - public int hashCode() { - int result = localpart.hashCode(); - result = 31 * result + domainpart.hashCode(); - result = 31 * result + resourcepart.hashCode(); - return result; - } - - public boolean hasLocalpart() { - return !localpart.isEmpty(); - } - - public boolean isBareJid() { - return this.resourcepart.isEmpty(); - } + public static Jid fromString(final String jid) throws InvalidJidException { + return new Jid(jid); + } + + public static Jid fromParts(final String localpart, + final String domainpart, + final String resourcepart) throws InvalidJidException { + String out; + if (localpart == null || localpart.isEmpty()) { + out = domainpart; + } else { + out = localpart + "@" + domainpart; + } + if (resourcepart != null && !resourcepart.isEmpty()) { + out = out + "/" + resourcepart; + } + return new Jid(out); + } + + private Jid(final String jid) throws InvalidJidException { + // Hackish Android way to count the number of chars in a string... should work everywhere. + final int atCount = jid.length() - jid.replace("@", "").length(); + final int slashCount = jid.length() - jid.replace("/", "").length(); + + // Throw an error if there's anything obvious wrong with the JID... + if (jid.isEmpty() || jid.length() > 3071) { + throw new InvalidJidException(InvalidJidException.INVALID_LENGTH); + } + + // Go ahead and check if the localpart or resourcepart is empty. + if (jid.startsWith("@") || jid.endsWith("@") || + jid.startsWith("/") || jid.endsWith("/")) { + throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); + } + + String finaljid; + + final int domainpartStart; + final int atLoc = jid.indexOf("@"); + final int slashLoc = jid.indexOf("/"); + // If there is no "@" in the JID (eg. "example.net" or "example.net/resource") + // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"): + if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) { + localpart = ""; + finaljid = ""; + domainpartStart = 0; + } else { + final String lp = jid.substring(0, atLoc); + try { + localpart = Stringprep.nodeprep(lp); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } + if (localpart.isEmpty() || localpart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + domainpartStart = atLoc + 1; + finaljid = lp + "@"; + } + + final String dp; + if (slashCount > 0) { + final String rp = jid.substring(slashLoc + 1, jid.length()); + try { + resourcepart = Stringprep.resourceprep(rp); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } + if (resourcepart.isEmpty() || resourcepart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES); + finaljid = finaljid + dp + "/" + rp; + } else { + resourcepart = ""; + dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()), + IDN.USE_STD3_ASCII_RULES); + finaljid = finaljid + dp; + } + + // Remove trailing "." before storing the domain part. + if (dp.endsWith(".")) { + try { + domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES); + } catch (final IllegalArgumentException e) { + throw new InvalidJidException(e); + } + } else { + try { + domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES); + } catch (final IllegalArgumentException e) { + throw new InvalidJidException(e); + } + } + + // TODO: Find a proper domain validation library; validate individual parts, separators, etc. + if (domainpart.isEmpty() || domainpart.length() > 1023) { + throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); + } + + this.displayjid = finaljid; + } + + public Jid toBareJid() { + try { + return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, ""); + } catch (final InvalidJidException e) { + // This should never happen. + return null; + } + } + + public Jid toDomainJid() { + try { + return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart()); + } catch (final InvalidJidException e) { + // This should never happen. + return null; + } + } + + @Override + public String toString() { + return displayjid; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Jid jid = (Jid) o; + + return jid.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + int result = localpart.hashCode(); + result = 31 * result + domainpart.hashCode(); + result = 31 * result + resourcepart.hashCode(); + return result; + } + + public boolean hasLocalpart() { + return !localpart.isEmpty(); + } + + public boolean isBareJid() { + return this.resourcepart.isEmpty(); + } } -- cgit v1.2.3 From 29a7828c5f7316713c625cbf1765a4f82457c0b6 Mon Sep 17 00:00:00 2001 From: Sam Whited Date: Tue, 2 Dec 2014 10:17:50 -0500 Subject: Allow JIDs such as "example.net/@" --- src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index cb37bde3..c40fa0b6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -70,10 +70,9 @@ public final class Jid { } // Go ahead and check if the localpart or resourcepart is empty. - if (jid.startsWith("@") || jid.endsWith("@") || - jid.startsWith("/") || jid.endsWith("/")) { + if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) { throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); - } + } String finaljid; -- cgit v1.2.3