package de.thedevstack.conversationsplus.xmpp.jid; import android.util.LruCache; import net.java.otr4j.session.SessionID; import java.net.IDN; import gnu.inet.encoding.Stringprep; import gnu.inet.encoding.StringprepException; /** */ public class JidUtil { private static LruCache cache = new LruCache<>(1024); public static Jid fromSessionID(final SessionID id) throws InvalidJidException { if (id.getUserID().isEmpty()) { return fromString(id.getAccountID()); } else { return fromString(id.getAccountID()+"/"+id.getUserID()); } } public static Jid fromString(final String jid) throws InvalidJidException { return fromString(jid, false); } public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException { if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL); Jid fromCache = JidUtil.cache.get(jid); if (fromCache != null) { return fromCache; } // 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("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) { throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER); } String finaljid; String localpart; String domainpart; String resourcepart; 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 = safe ? lp : 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 = safe ? rp : 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); } try { dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES); } catch (final StringprepException e) { throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); } finaljid = finaljid + dp + "/" + rp; } else { resourcepart = ""; try{ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES); } catch (final StringprepException e) { throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); } 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); } Jid createdJid = new Jid(finaljid, localpart, domainpart, resourcepart); JidUtil.cache.put(jid, createdJid); return createdJid; } 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 JidUtil.fromString(out, false); } }