Merge pull request #740 from SamWhited/jidrpfix

Fix RFC 6122 implementation
This commit is contained in:
Daniel Gultsch 2014-12-03 00:12:36 +01:00
commit 138345c5bf

View file

@ -12,25 +12,25 @@ import gnu.inet.encoding.StringprepException;
*/ */
public final class Jid { public final class Jid {
private final String localpart; private final String localpart;
private final String domainpart; private final String domainpart;
private final String resourcepart; private final String resourcepart;
// It's much more efficient to store the ful JID as well as the parts instead of figuring them // 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). // all out every time (since some characters are displayed but aren't used for comparisons).
private final String displayjid; private final String displayjid;
public String getLocalpart() { public String getLocalpart() {
return localpart; return localpart;
} }
public String getDomainpart() { public String getDomainpart() {
return IDN.toUnicode(domainpart); return IDN.toUnicode(domainpart);
} }
public String getResourcepart() { public String getResourcepart() {
return resourcepart; return resourcepart;
} }
public static Jid fromSessionID(SessionID id) throws InvalidJidException{ public static Jid fromSessionID(SessionID id) throws InvalidJidException{
if (id.getUserID().isEmpty()) { if (id.getUserID().isEmpty()) {
@ -40,152 +40,154 @@ public final class Jid {
} }
} }
public static Jid fromString(final String jid) throws InvalidJidException { public static Jid fromString(final String jid) throws InvalidJidException {
return new Jid(jid); return new Jid(jid);
} }
public static Jid fromParts(final String localpart, public static Jid fromParts(final String localpart,
final String domainpart, final String domainpart,
final String resourcepart) throws InvalidJidException { final String resourcepart) throws InvalidJidException {
String out; String out;
if (localpart == null || localpart.isEmpty()) { if (localpart == null || localpart.isEmpty()) {
out = domainpart; out = domainpart;
} else { } else {
out = localpart + "@" + domainpart; out = localpart + "@" + domainpart;
} }
if (resourcepart != null && !resourcepart.isEmpty()) { if (resourcepart != null && !resourcepart.isEmpty()) {
out = out + "/" + resourcepart; out = out + "/" + resourcepart;
} }
return new Jid(out); return new Jid(out);
} }
private Jid(final String jid) throws InvalidJidException { private Jid(final String jid) throws InvalidJidException {
// Hackish Android way to count the number of chars in a string... should work everywhere. // 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 atCount = jid.length() - jid.replace("@", "").length();
final int slashCount = 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... // Throw an error if there's anything obvious wrong with the JID...
if (jid.isEmpty() || jid.length() > 3071) { if (jid.isEmpty() || jid.length() > 3071) {
throw new InvalidJidException(InvalidJidException.INVALID_LENGTH); throw new InvalidJidException(InvalidJidException.INVALID_LENGTH);
} }
if (atCount > 1 || slashCount > 1 ||
jid.startsWith("@") || jid.endsWith("@") ||
jid.startsWith("/") || jid.endsWith("/")) {
throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
}
String finaljid; // Go ahead and check if the localpart or resourcepart is empty.
if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) {
throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
}
final int domainpartStart; String finaljid;
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; final int domainpartStart;
if (slashCount == 1) { final int atLoc = jid.indexOf("@");
final int slashLoc = jid.indexOf("/"); final int slashLoc = jid.indexOf("/");
final String rp = jid.substring(slashLoc + 1, jid.length()); // If there is no "@" in the JID (eg. "example.net" or "example.net/resource")
try { // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
resourcepart = Stringprep.resourceprep(rp); if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
} catch (final StringprepException e) { localpart = "";
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); finaljid = "";
} domainpartStart = 0;
if (resourcepart.isEmpty() || resourcepart.length() > 1023) { } else {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); final String lp = jid.substring(0, atLoc);
} try {
dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES); localpart = Stringprep.nodeprep(lp);
finaljid = finaljid + dp + "/" + rp; } catch (final StringprepException e) {
} else { throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
resourcepart = ""; }
dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()), if (localpart.isEmpty() || localpart.length() > 1023) {
IDN.USE_STD3_ASCII_RULES); throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
finaljid = finaljid + dp; }
} domainpartStart = atLoc + 1;
finaljid = lp + "@";
}
// Remove trailing "." before storing the domain part. final String dp;
if (dp.endsWith(".")) { if (slashCount > 0) {
try { final String rp = jid.substring(slashLoc + 1, jid.length());
domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES); try {
} catch (final IllegalArgumentException e) { resourcepart = Stringprep.resourceprep(rp);
throw new InvalidJidException(e); } catch (final StringprepException e) {
} throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
} else { }
try { if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES); throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
} catch (final IllegalArgumentException e) { }
throw new InvalidJidException(e); 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;
}
// TODO: Find a proper domain validation library; validate individual parts, separators, etc. // Remove trailing "." before storing the domain part.
if (domainpart.isEmpty() || domainpart.length() > 1023) { if (dp.endsWith(".")) {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); 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);
}
}
this.displayjid = finaljid; // 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);
}
public Jid toBareJid() { this.displayjid = finaljid;
try { }
return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
} catch (final InvalidJidException e) {
// This should never happen.
return null;
}
}
public Jid toDomainJid() { public Jid toBareJid() {
try { try {
return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart()); return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
} catch (final InvalidJidException e) { } catch (final InvalidJidException e) {
// This should never happen. // This should never happen.
return null; return null;
} }
} }
@Override public Jid toDomainJid() {
public String toString() { try {
return displayjid; return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart());
} } catch (final InvalidJidException e) {
// This should never happen.
return null;
}
}
@Override @Override
public boolean equals(final Object o) { public String toString() {
if (this == o) return true; return displayjid;
if (o == null || getClass() != o.getClass()) return false; }
final Jid jid = (Jid) o; @Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return jid.hashCode() == this.hashCode(); final Jid jid = (Jid) o;
}
@Override return jid.hashCode() == this.hashCode();
public int hashCode() { }
int result = localpart.hashCode();
result = 31 * result + domainpart.hashCode();
result = 31 * result + resourcepart.hashCode();
return result;
}
public boolean hasLocalpart() { @Override
return !localpart.isEmpty(); public int hashCode() {
} int result = localpart.hashCode();
result = 31 * result + domainpart.hashCode();
result = 31 * result + resourcepart.hashCode();
return result;
}
public boolean isBareJid() { public boolean hasLocalpart() {
return this.resourcepart.isEmpty(); return !localpart.isEmpty();
} }
public boolean isBareJid() {
return this.resourcepart.isEmpty();
}
} }