All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jxmpp.util.XmppStringUtils Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
/**
 *
 * Copyright © 2014-2019 Florian Schmaus
 *
 * 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.jxmpp.util;

import org.jxmpp.util.cache.LruCache;

/**
 * Utility class for handling Strings in XMPP.
 */
public class XmppStringUtils {

	/**
	 * Returns the localpart of an XMPP address (JID). For example, for the address "[email protected]/Resource", "user"
	 * would be returned. Returns null if the given JID has no localpart. Returns the empty string if
	 * the given JIDs localpart is the empty string (which is invalid).
	 * 
	 * @param jid
	 *            the XMPP address to parse.
	 * @return the name portion of the XMPP address, the empty String or null.
	 */
	public static String parseLocalpart(String jid) {
		int atIndex = jid.indexOf('@');
		if (atIndex < 0) {
			return null;
		}
		if (atIndex == 0) {
			// '@' as first character, i.e. '@example.org". Return empty string as
			// localpart, to make it possible to differentiate this from 'example.org'
			// (which would return 'null' as localpart).
			return "";
		}

		int slashIndex = jid.indexOf('/');
		if (slashIndex >= 0 && slashIndex < atIndex) {
			// This is an '@' character in the resourcepart.
			return null;
		}

		return jid.substring(0, atIndex);
	}

	/**
	 * Returns the domain of an XMPP address (JID). For example, for the address "[email protected]/Resource", "xmpp.org"
	 * would be returned. If jid is null, then this method returns also null. If
	 * the input String is no valid JID or has no domainpart, then this method will return the empty String.
	 * 
	 * @param jid
	 *            the XMPP address to parse.
	 * @return the domainpart of the XMPP address, the empty String or null.
	 */
	public static String parseDomain(String jid) {
		if (jid == null) return null;

		int atIndex = jid.indexOf('@');
		int slashIndex = jid.indexOf('/');
		if (slashIndex >= 0) {
			// '[email protected]/resource' and '[email protected]/res@otherres' case
			if (slashIndex > atIndex) {
				return jid.substring(atIndex + 1, slashIndex);
			// 'domain.foo/res@otherres' case
			} else {
				return jid.substring(0, slashIndex);
			}
		} else {
			return jid.substring(atIndex + 1);
		}
	}

	/**
	 * Returns the resource portion of an XMPP address (JID). For example, for the address "[email protected]/Resource",
	 * "Resource" would be returned. Returns null if the given JID has no resourcepart. Returns the
	 * empty string if the given JID has an empty resourcepart (which is invalid).
	 * 
	 * @param jid
	 *            the XMPP address to parse.
	 * @return the resource portion of the XMPP address.
	 */
	public static String parseResource(String jid) {
		int slashIndex = jid.indexOf('/');
		if (slashIndex < 0) {
			return null;
		}
		if (slashIndex + 1 > jid.length()) {
			return "";
		} else {
			return jid.substring(slashIndex + 1);
		}
	}

	/**
	 * Returns the JID with any resource information removed. For example, for
	 * the address "[email protected]/Smack", "[email protected]" would
	 * be returned.
	 * 
	 * @param jid
	 *            the XMPP JID.
	 * @return the bare XMPP JID without resource information.
	 */
	public static String parseBareJid(String jid) {
		int slashIndex = jid.indexOf('/');
		if (slashIndex < 0) {
			return jid;
		} else if (slashIndex == 0) {
			return "";
		} else {
			return jid.substring(0, slashIndex);
		}
	}

	/**
	 * Returns true if jid is a full JID (i.e. a JID with resource part).
	 * 
	 * @param jid the String to check.
	 * @return true if full JID, false otherwise
	 */
	public static boolean isFullJID(String jid) {
		String domain = parseDomain(jid);
		String resource = parseResource(jid);
		if ((domain == null || domain.length() <= 0)
				|| (resource == null || resource.length() <= 0)) {
			return false;
		}
		return true;
	}

	/**
	 * Returns true if jid is a bare JID ("[email protected]").
	 * 

* This method may return true for Strings that are not valid JIDs (e.g. because of Stringprep violations). Consider * using org.jxmpp.jid.util.JidUtil.validateBareJid(String) from jxmpp-jid instead of this method as it * exceptions provide a meaningful message string why the JID is not a bare JID and will also check for Stringprep * errors. *

* * @param jid the String to check. * @return true if bare JID, false otherwise */ public static boolean isBareJid(String jid) { String domain = parseDomain(jid); String resource = parseResource(jid); return (domain != null && domain.length() > 0 && (resource == null || resource.length() == 0)); } private static final LruCache LOCALPART_ESCAPE_CACHE = new LruCache(100); private static final LruCache LOCALPART_UNESCAPE_CACHE = new LruCache(100); /** * Escapes the localpart of a JID according to "JID Escaping" (XEP-0106). * Escaping replaces characters prohibited by Nodeprep with escape sequences, * as follows: * * * * * * * * * * * * * *
Character mappings
Unescaped CharacterEncoded Sequence
<space>\20
"\22
&\26
'\27
/\2f
:\3a
<\3c
>\3e
@\40
\\5c
* *

* This process is useful when the localpart comes from an external source that doesn't * conform to Nodeprep. For example, a username in LDAP may be "Joe Smith". Because * the <space> character isn't a valid part of a localpart, the username should * be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\[email protected]" * after case-folding, etc. has been applied). *

* * All localpart escaping and un-escaping must be performed manually at the appropriate * time; the JID class will not escape or un-escape automatically. * * @param localpart the localpart. * @return the escaped version of the localpart. * @see XEP-106: JID Escaping */ public static String escapeLocalpart(String localpart) { if (localpart == null) { return null; } String res = LOCALPART_ESCAPE_CACHE.lookup(localpart); if (res != null) { return res; } StringBuilder buf = new StringBuilder(localpart.length() + 8); for (int i = 0, n = localpart.length(); i < n; i++) { char c = localpart.charAt(i); switch (c) { case '"': buf.append("\\22"); break; case '&': buf.append("\\26"); break; case '\'': buf.append("\\27"); break; case '/': buf.append("\\2f"); break; case ':': buf.append("\\3a"); break; case '<': buf.append("\\3c"); break; case '>': buf.append("\\3e"); break; case '@': buf.append("\\40"); break; case '\\': buf.append("\\5c"); break; default: { if (Character.isWhitespace(c)) { buf.append("\\20"); } else { buf.append(c); } } } } res = buf.toString(); LOCALPART_ESCAPE_CACHE.put(localpart, res); return res; } /** * Un-escapes the localpart of a JID according to "JID Escaping" (XEP-0106). * Escaping replaces characters prohibited by Nodeprep with escape sequences, * as follows: * * * * * * * * * * * * * * *
Character mapping
Unescaped CharacterEncoded Sequence
<space>\20
"\22
&\26
'\27
/\2f
:\3a
<\3c
>\3e
@\40
\\5c
* *

* This process is useful when the localpart comes from an external source that doesn't * conform to Nodeprep. For example, a username in LDAP may be "Joe Smith". Because * the <space> character isn't a valid part of a localpart, the username should * be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\[email protected]" * after case-folding, etc. has been applied). *

* * All localpart escaping and un-escaping must be performed manually at the appropriate * time; the JID class will not escape or un-escape automatically. * * @param localpart the escaped version of the localpart. * @return the un-escaped version of the localpart. * @see XEP-106: JID Escaping */ public static String unescapeLocalpart(String localpart) { if (localpart == null) { return null; } String res = LOCALPART_UNESCAPE_CACHE.lookup(localpart); if (res != null) { return res; } char[] localpartChars = localpart.toCharArray(); StringBuilder buf = new StringBuilder(localpartChars.length); for (int i = 0, n = localpartChars.length; i < n; i++) { compare: { char c = localpart.charAt(i); if (c == '\\' && i + 2 < n) { char c2 = localpartChars[i + 1]; char c3 = localpartChars[i + 2]; switch(c2) { case '2': switch (c3) { case '0': buf.append(' '); i += 2; break compare; case '2': buf.append('"'); i += 2; break compare; case '6': buf.append('&'); i += 2; break compare; case '7': buf.append('\''); i += 2; break compare; case 'f': buf.append('/'); i += 2; break compare; } break; case '3': switch (c3) { case 'a': buf.append(':'); i += 2; break compare; case 'c': buf.append('<'); i += 2; break compare; case 'e': buf.append('>'); i += 2; break compare; } break; case '4': if (c3 == '0') { buf.append("@"); i += 2; break compare; } break; case '5': if (c3 == 'c') { buf.append("\\"); i += 2; break compare; } break; } } buf.append(c); } } res = buf.toString(); LOCALPART_UNESCAPE_CACHE.put(localpart, res); return res; } /** * Construct a JID String from the given parts. * * @param localpart the localpart. * @param domainpart the domainpart. * @return the constructed JID String. */ public static String completeJidFrom(CharSequence localpart, CharSequence domainpart) { return completeJidFrom(localpart != null ? localpart.toString() : null, domainpart.toString()); } /** * Construct a JID String from the given parts. * * @param localpart the localpart. * @param domainpart the domainpart. * @return the constructed JID String. */ public static String completeJidFrom(String localpart, String domainpart) { return completeJidFrom(localpart, domainpart, null); } /** * Construct a JID String from the given parts. * * @param localpart the localpart. * @param domainpart the domainpart. * @param resource the resourcepart. * @return the constructed JID String. */ public static String completeJidFrom(CharSequence localpart, CharSequence domainpart, CharSequence resource) { return completeJidFrom(localpart != null ? localpart.toString() : null, domainpart.toString(), resource != null ? resource.toString() : null); } /** * Construct a JID String from the given parts. * * @param localpart the localpart. * @param domainpart the domainpart. * @param resource the resourcepart. * @return the constructed JID String. */ public static String completeJidFrom(String localpart, String domainpart, String resource) { if (domainpart == null) { throw new IllegalArgumentException("domainpart must not be null"); } int maxResLength = domainpart.length(); if (localpart != null) maxResLength += localpart.length() + 1; if (resource != null) maxResLength += resource.length() + 1; StringBuilder sb = new StringBuilder(maxResLength); if (localpart != null) { sb.append(localpart).append('@'); } sb.append(domainpart); if (resource != null) { sb.append('/').append(resource); } return sb.toString(); } /** * Generate a unique key from a element name and namespace. This key can be used to lookup element/namespace * information. The key is simply generated by concatenating the strings as follows: * element + '\t' + namespace. *

* The tab character (\t) was chosen because it will be normalized, i.e. replace by space, in attribute values. It * therefore should never appear in element or namespace. For more information about the * normalization, see the XML specification § 3.3.3 * Attribute-Value Normalization. *

* * @param element the element. * @param namespace the namespace. * @return the unique key of element and namespace. */ public static String generateKey(String element, String namespace) { return element + '\t' + namespace; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy